The purpose of this notebook is to demonstrate some of what is possible for the mining and visualisation of a text. We’ll go through some of the basic tools and do some data visualisation to demonstrate what is possible. If this kind of analysis is not directly relevant to your research, hopefully you can use this time to practice different data visualisation techniques and become more familiar with seeking out answers to questions about programming R code.

1 Setting up

In this session, we’ll be using a few packages, but they’ll be introduced as we go. Firstly, we’ll need to load in tidyverse and tm (text mining). We will not explicitly be using SnowballC at this time, but it may be useful to know about as it helps with lemmatization for word frequency analysis in several languages. It is one of tm’s dependencies, and may be doing a bit of background work.

library(tidyverse)
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.5
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
# R must be at least 3.3.1 for `tm` and `slam` to work.
# library("SnowballC")
library(tm)
Loading required package: NLP

Attaching package: ‘NLP’

The following object is masked from ‘package:ggplot2’:

    annotate

Before we get started working with the actual data, we’re going to create a custom function for plotting multiple ggplot graphs in a single image. This function was copied from Cookbook for R.

# Multiple plot function
#
# ggplot objects can be passed in ..., or to plotlist (as a list of ggplot objects)
# - cols:   Number of columns in layout
# - layout: A matrix specifying the layout. If present, 'cols' is ignored.
#
# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE),
# then plot 1 will go in the upper left, 2 will go in the upper right, and
# 3 will go all the way across the bottom.
#
multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
  library(grid)
  # Make a list from the ... arguments and plotlist
  plots <- c(list(...), plotlist)
  numPlots = length(plots)
  # If layout is NULL, then use 'cols' to determine layout
  if (is.null(layout)) {
    # Make the panel
    # ncol: Number of columns of plots
    # nrow: Number of rows needed, calculated from # of cols
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                    ncol = cols, nrow = ceiling(numPlots/cols))
  }
 if (numPlots==1) {
    print(plots[[1]])
  } else {
    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))
    # Make each plot, in the correct location
    for (i in 1:numPlots) {
      # Get the i,j matrix positions of the regions that contain this subplot
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))
      print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}

2 Reading in corpus

For this demonstration, I’ve selected a corpus of Shakespeare’s plays and adapted some code from a Kaggle notebook. Once you understand what the code here is doing, it can easily be adapted to a corpus of any structure or content.

We should take a look at how the corpus is structured so we know what we’re dealing with.

shak <- read.csv("../data/Shakespeare_data.csv",header = TRUE, as.is = TRUE)

These are the first ten lines of the corpus.

Each column is labeled and the content of the column is consistent for each row (all 111396 of them!). Some of the rows may not be useful. Some contain empty cells (labeled NA). Some contain a lot of information and we might need to do some processing on them before we can use the information quantitatively (for example the column PlayerLine).

3 Word frequency

The first thing we’ll look at is word frequency, or how often a string (in this case “love”) occurs in the data frame. To do this, we must identify every time the word “love” appears and highlight it in a way so that it can be counted based on different properties of its environment (e.g., by play, by player, by scene, etc).

Here are the first 10 rows of a data frame that contains the number of times “love” appears in each play. It’s been sorted in descending order, but doesn’t contain any other information about where and when the word occurs.

# play level word frequency
plays <- unique(shak$Play)
loveFreq<-numeric()
for (i in 1:length(plays)){
    text <- Corpus(VectorSource(paste(shak[shak$Play==plays[i],]$PlayerLine,collapse=" ")))
    text <- tm_map(text, removePunctuation)
    text <- tm_map(text, PlainTextDocument)
    text <- tm_map(text, removeWords, stopwords('english'))
    
    # stemming to merge all "loved", "loving" into one   
    text <- tm_map(text, stemDocument)
    tdm  <- TermDocumentMatrix(text)
    
    loveFreq[i]<-as.numeric(slam::row_sums(tdm)["love"]) # `slam` must be installed for this to work
  }
lPlay <- data.frame(plays,loveFreq)
lPlay <- na.omit(lPlay)
# order the plays based on the occurence of love
lPlay<-lPlay[order(-lPlay$loveFreq),]
lPlay

We can also look at which players say “love” the most over the course of their appearences. These are only the top 10 players who use the word “love” most.

# player level word frequency
players <- unique(shak$Player)
loveFreq <- numeric()
for (i in 1:length(players)){
    text <- Corpus(VectorSource(paste(shak[shak$Player==players[i],]$PlayerLine,collapse=" ")))
    text <- tm_map(text, removePunctuation)
    text <- tm_map(text, PlainTextDocument)
    text <- tm_map(text, removeWords, stopwords('english'))
    text <- tm_map(text,stemDocument)
    
    tdm  <- TermDocumentMatrix(text)
    
    loveFreq[i] <- as.numeric(slam::row_sums(tdm)["love"])
  }
lPlayer <- data.frame(players,loveFreq)
lPlayer <- na.omit(lPlayer)
#order
lPlayer <- lPlayer[order(-lPlayer$loveFreq),]
lPlayer

3.1 Exploring someone else’s code

Let’s go through the for-loop in more detail to understand what it’s doing.

How many players are in this vector? (Answer: 935)
That’s the number of times this for-loop will cycle, each time incrementing the loop number by 1.

for (i in 1:length(players)){

The first line of the loop uses Corpus() and VectorSource(). What do these functions do?
We can check the Help files by typing ?VectorSource(). It looks like both deal with the structure and metadata of a corpus. Importantly, we can see [i] in the code though. This is the same i from the previous line, which cycles through each of the players in the vector.
What does that tell us about the first pass through this loop?

text <- Corpus(VectorSource(paste(shak[shak$Player==players[i],]$PlayerLine,collapse=" ")))

The next few lines all use the function tm_map(), which performs a transformation on the corpus. In this case, the corpus is the subset we’ve called text in the above line. Judging from the syntax, it appears that they respectively remove punctuation from the text, convert it to a plain text document, remove English stop words, and merge all words with the same stems or lemmas (i.e., “love”, “loving”, “loved”, etc).

text <- tm_map(text, removePunctuation)
text <- tm_map(text, PlainTextDocument)
text <- tm_map(text, removeWords, stopwords('english'))
text <- tm_map(text, stemDocument)

This line turns the resulting vector into a term-document matrix, which is basically a way of identifying where each word occurs numerically, which will allow us to count the words in the next line.

tdm  <- TermDocumentMatrix(text)

A simple TDM may look like this:

enter exit hamlet ophelia and
text_1 1 0 1 0 0
text_2 1 0 0 1 0
text_3 0 1 1 1 1

What might these three “texts” say?

The last line has several things going on. We can figure out what each function does by trying it on its own.

loveFreq[i] <- as.numeric(slam::row_sums(tdm)["love"])

First, we can see what tdm looks like when i = 1:

tdm
<<TermDocumentMatrix (terms: 27, documents: 2)>>
Non-/sparse entries: 27/27
Sparsity           : 50%
Maximal term length: 12
Weighting          : term frequency (tf)

Hmmm, that doesn’t look interpretable by a human. Maybe row_sums() from the package slam will help?

slam::row_sums(tdm)
         act       adjoin    antechamb        apart     bardolph        blunt 
           1            1            1            1            1            1 
   boarshead       cloten         earl    eastcheap        enter     falstaff 
           1            1            1            1            3            1 
       henri       imogen         john         king    lancaster       london 
           1            1            1            1            1            1 
        lord        other        palac        scene          sir       tavern 
           2            1            1            1            1            1 
         the       walter westmoreland 
           2            1            1 

Ah ha! That looks more intelligible. Which player is this for?
(Answer = ; if you want to see what different players are doing, you can change the value of i by hand and re-run the code.)

So what is ["love"] doing in the code?

slam::row_sums(tdm)["love"]
<NA> 
  NA 

Ah, “love” doesn’t exist in this row (where i = 1). Let’s see what it would look like if we were actually interested in “enter”:

slam::row_sums(tdm)["enter"]
enter 
    3 

There are two elements that are being printed out here: the word (“enter”) and the sum (3). That’s what as.numeric() will help us with:

as.numeric(slam::row_sums(tdm)["enter"])
[1] 3

This means that the sum of occurrences of “love” is now entered into index [i] in the vector loveFreq, and the loop begins again until i = length(players).

Finally, we close off the for-loop.

}

Can you figure out how to use the code above to create a data frame with the following two conditions?:

  • A list of players
  • Ordered by frequency of the word death
# create the vectors you need
# create the for-loop to calculate frequency
# create the data frame
# order the data frame from most to least frequent

(possible solution below)

























# create the vectors you need
# players <- unique(shak$Player) # <-- we already made this one before, but we can do it again too
deathFreq <- numeric() # <-- create a vector of class "numeric"

# create the for-loop to calculate frequency
for (i in 1:length(players)){
    text <- Corpus(VectorSource(paste(shak[shak$Player==players[i],]$PlayerLine,collapse=" ")))
    text <- tm_map(text, removePunctuation)
    text <- tm_map(text, PlainTextDocument)
    text <- tm_map(text, removeWords, stopwords('english'))
    text <- tm_map(text,stemDocument)
    
    tdm  <- TermDocumentMatrix(text)
    
    deathFreq[i] <- as.numeric(slam::row_sums(tdm)["death"])
  }

# create the data frame
dPlayer <- data.frame(players,deathFreq)
dPlayer <- na.omit(dPlayer)
# order the data frame from most to least frequent
dPlayer <- dPlayer[order(-dPlayer$deathFreq),]
dPlayer

4 Visualising a corpus

Maybe you are curious how the longer and shorter plays compare in word frequency. Instead of hand-counting each, we can graph and order them. Based on this graph, you don’t need to know exactly how long each is, but you can see that Othello is much longer than Loves Labours Lost, which can inform how you approach the comparison later on.

shak %>%
  # `Play` is the factor we are interested in
  group_by(Play) %>% 
  # summarise the content of `Play` by counting how many instances there are of each (`n`)
  summarise(n = n()) %>% 
  # reorder() allows us to sort them greatest to least
  ggplot(., aes(x=reorder(Play, n),y=n)) + 
    # stat="identity" is crucial for ggplot to know each `n` is the length of the bar
    geom_bar(stat="identity") + 
    # this way, the play names will be horizontal and fully displayed
    coord_flip() + 
    ggtitle("Length of Shakespeare's plays") +
    xlab("Play") +
    ylab("Number of lines")

4.1 Perception of frequency

Within a single play, maybe we want to know which characters are the chattiest. We can visualise the number of lines of text per character to get a sense of who is dominating the stage.

shak %>%
  filter(Play == "Hamlet") %>% # limit our dataset to only the play "Hamlet"
  group_by(Player) %>%
  summarise(n = n()) %>%
  ggplot(., aes(x=reorder(Player, n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("Speech in Hamlet") +
    xlab("Player") +
    ylab("Number of lines") #+

    #scale_y_log10()

We can also look across plays for frequency. By comparing which plays have the word “love” the most often, we might be able to group them (perceptually) into plays about love and those that are not. Maybe?

lPlay %>%
  ggplot(., aes(x=reorder(plays, loveFreq),y=loveFreq)) +
    geom_bar(aes(),stat="identity") +
    coord_flip() +
    ggtitle("Love in each play") +
#    theme(legend.position="none") +
    xlab("Play") +
    ylab("frequency of the word 'love'") +
    theme(legend.position = "none")

4.2 Comparing across subsets

One thing that graphs can do very easily is give you a way to identify trends when you sort events (e.g., plays) into multiple different categories. For instance, the frequency graph above is interesting, but there are so many plays and as a non-expert, I can’t tell you what each is about, what style it is written in, or whether I’d expect it to be about “love” or not. So, we can add another dimension of information.

lPlayCat <- lPlay

# create boolean vectors for four play categories
comedies <- c("A Comedy of Errors",
            "As you like it",
            "Alls well that ends well",
            "Loves Labours Lost",
            "Measure for measure",
            "Merchant of Venice",
            "Merry Wives of Windsor",
            "A Midsummer nights dream",
            "Much Ado about nothing",
            "Taming of the Shrew",
            "Twelfth Night",
            "Two Gentlemen of Verona")
romances <- c("Pericles",
             "Cymbeline",
             "A Winters Tale",
             "The Tempest")
histories <- c("King John",
             "Richard II",
             "Richard III",
             "Henry IV",
             "Henry V",
             "Henry VI Part 1",
             "Henry VI Part 2",
             "Henry VI Part 3",
             "Henry VIII",
             "Coriolanus",
             "Julius Caesar",
             "Antony and Cleopatra",
             "King Lear",
             "macbeth")
tragedies <- c("Titus Andronicus",
             "Romeo and Juliet",
             "Hamlet",
             "Troilus and Cressida",
             "Othello",
             "Timon of Athens")
# create boolean vectors for four play categories
comedy <- lPlayCat$plays %in% comedies
romance <- lPlayCat$plays %in% romances
history <- lPlayCat$plays %in% histories
tragedy <- lPlayCat$plays %in% tragedies

# create new column with these four categories
lPlayCat <- lPlay %>%
  mutate(category = case_when(comedy == TRUE ~ "comedy",
                              romance == TRUE ~ "romance",
                              history == TRUE ~ "history",
                              tragedy == TRUE ~ "tragedy"))

In the following graph, each color represents a different category (as determined by Wikipedia’s First Folio page, plus information about the “late romances”). Now, we can see if there are trends for different categories to mention “love” more or less than the others.

lPlayCat %>%
  ggplot(., aes(x=reorder(plays, loveFreq),y=loveFreq)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Love in each play") +
#    theme(legend.position="none") +
    xlab("Play") +
    ylab("frequency of the word 'love'")

It seems to me that comedies and tragedies discuss “love” the most, whereas histories and the late romances discuss it the least. Is this intuitive? Maybe. But there’s a problem. A Comedy of Errors has the fewest mentions of “love”, but it’s also the shortest play, so it has the fewest words overall. What we really want to see is the proportion of “love”-frequency per play, not the raw counts. To do that, we have to add in the total length of each play to the data frame, but we’ve lost that information in lPlayCat and lPlay. How might you add that information in to the lPlayCat dataframe?

(possible solution below)

























# create new data frame for lengths of plays
playLength <- shak %>%
  group_by(Play) %>%
  summarise(n = n()) %>%
  transmute(plays=Play,length=n) # rename columns that we want, which happens to be both of them
# add column to lPlayCat
lPlayCatLen <- lPlayCat %>%
  left_join(.,playLength)
Joining, by = "plays"
Column `plays` joining factor and character vector, coercing into character vector

Now we can take a look to see how raw and proportional graphs compare.

lPlayCatLen %>%
  mutate(proportion = loveFreq/length) %>%
  ggplot(., aes(x=reorder(plays, proportion),y=proportion)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Love in each play") +
#    theme(legend.position="none") +
    xlab("Play") +
    ylab("proportional frequency of the word 'love'")

Not a whole lot has changed, but I think the distribution of comedies and tragedies is even more pronounced. And, we have more information about A Comedy of Errors, which is still very close to the bottom of the graph. Not every comedy is about love, it seems.

Finally, we can generate these same types of graphs for different subgroups, too. We’ll have to add in $category to shak as well, but we already have the vectors for each level of category, so it’s pretty easy to do.

# create boolean vectors for four play categories in `shak`
comedy  <- shak$Play %in% comedies
romance <- shak$Play %in% romances
history <- shak$Play %in% histories
tragedy <- shak$Play %in% tragedies
# create new column with these four categories
shakCat <- shak %>%
  mutate(category = case_when(comedy == TRUE ~ "comedy",
                              romance == TRUE ~ "romance",
                              history == TRUE ~ "history",
                              tragedy == TRUE ~ "tragedy"))

Here’s one example, where we look at the number of lines each player has, focusing only on players who have greater than 700 lines. We can also see if there are any trends in these top speakers by play category. It seems to me that the histories dominate, but Hamlet and Iago dominate the scene (so to speak).

shakCat %>%
  group_by(Play,Player,category) %>%
  summarise(n = n()) %>%
  filter(n > 700) %>%
  ggplot(., aes(x=reorder(Player, n),y=n)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Number of lines by character") +
#    theme(legend.position="none") +
    xlab("Player") +
    ylab("Number of lines")

Is this because histories and tragedies tend to be longer plays, overall? Quite possibly:

shakCat %>%
  group_by(Play,category) %>%
  summarise(n = n()) %>%
  ggplot(., aes(x=reorder(Play, n),y=n)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Length of Shakespeare's plays") +
    theme(legend.position="none") +
    xlab("Play") +
    ylab("Number of lines")

5 N-grams

Single words might not be able to tell us much about the texts, which is why examining collocations is such a popular technique. What’s the difference between “the king”, “kill the king”, and “kiss the king”? A lot, but we won’t know if we only look for instances of “king”. There is where n-grams become useful. N-grams are sets of adjacent words, calculated by assigning a number to ‘n’. That is, if we want sets of two words, we talk about bigrams. If we want sets of three words, we talk about trigrams.

5.1 Pre-processing the corpus

First, we can look at the corpus as a list of words, rather than a list of lines (by act and scene). What is unnest_tokens() doing here?

Now, we can automate the process of counting how often each word occurs. But, of course, certain words are going to be extremely common, and those words are unlikely to be informative.

Once we’ve filtered out our stop-words, the most frequent words look quite different.

shak %>%
  unnest_tokens(input = PlayerLine, output = word) %>%
  anti_join(stop_words, by = "word") %>%
  count(word, sort = TRUE)

However, Shakespeare uses a lot of words that aren’t in our default stop-word list, so we can append our own custom list.

word <- c(NA,"thou","thee","thy","thine","dost","shalt","wilt","hast","hath","scene","tis","ii","iii","iv","v","vi","vii")
lexicon <- rep("shakespeare",length(word))
new_stop <- cbind(word,lexicon)
shak_stop <- rbind(new_stop,stop_words)

Here is a visualisation of how stop words affect the corpus.

p1 <- shak %>%
  as_tibble(.) %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  count(word, sort = TRUE) %>%
  filter(n>800) %>%
  ggplot(., aes(x=reorder(word,n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("No stop words")
p2 <- shak %>%
  as_tibble(.) %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE) %>%
  filter(n>800) %>%
  ggplot(., aes(x=reorder(word,n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("Default stop words")
p3 <- shak %>%
  as_tibble(.) %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  anti_join(shak_stop) %>%
  count(word, sort = TRUE) %>%
  filter(n>800) %>%
  ggplot(., aes(x=reorder(word,n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("Custom stop words")
Column `word` joining character vector and factor, coercing into character vector
multiplot(p1,p2,p3,cols=3)

5.2 Comparing across plays

Before we try to compare across plays, let’s see what the most common bigrams are overall (after being filtered by the custom stop words list).

shak %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE)

5.2.1 1-grams (just regular token frequency)

If we choose a subset of words, we can look at how they are distributed across different plays. In this case, we can compare death, king, love, and sweet across six plays. Unsurprisingly, Romeo and Juliet uses the word love more than any other play, although Midsummer Night’s Dream is close. King is also much more common in plays about kings (surprise, surprise).

shak[,c(2,5,6)] %>%
  as_tibble() %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  filter(word=="love" | word =="king" | word=="death" | word=="sweet") %>%
  group_by(Player,Play,word) %>%
  summarise(n=n()) %>%
  filter(  Play == "Hamlet" | 
           Play == "King Lear" | 
           Play == "A Midsummer nights dream" | 
           Play == "Othello" | 
           Play == "Henry V" | 
           Play == "Romeo and Juliet") %>%
  arrange(desc(n)) %>%
  ggplot(., aes(x=word,y=n)) +
    geom_bar(aes(fill=word),stat="identity") +
    facet_wrap(~Play)

But there are more interesting words you could compare, certainly. These are just one example.

5.2.2 Bigrams

It’s probably much more interesting to look at collocation than simple word frequency. After all, it gives more context. However, it also reduces the number of tokens substantially.

Here, we can visualise three pairs of gendered noun phrases:

Masculine Feminine
my lord my lady
my father my mother
my husband my wife

What kinds of information can we see in the graph?

shak %>%
  as_tibble() %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  filter(bigram=="my lord" | bigram =="my lady" | bigram=="my mother" | bigram=="my father" | bigram=="my wife" | bigram=="my husband") %>%
  mutate(gender = bigram) %>%
  mutate(gender = recode_factor(gender,
                `my lord`="masc",
                `my father`="masc",
                `my husband`="masc",
                `my lady`="fem",
                `my mother`="fem",
                `my wife`="fem")) %>%
  group_by(Player,Play,bigram,gender) %>%
  summarise(n=n()) %>%
  mutate(bigramFac = factor(bigram, levels=c("my lord", "my husband", "my father", "my lady", "my wife", "my mother"))) %>%
  # too boring
  filter(  Play != "Henry VI Part 1" &
           Play != "Henry VI Part 2" &
           Play != "Henry VI Part 3" &
           Play != "Pericles" & 
           Play != "Timon of Athens" & 
           Play != "The Tempest") %>%
  # too skewed
  filter(  Play != "Hamlet" &
           Play != "Troilus and Cressida" &
           Play != "Richard III" &
           Play != "Titus Andronicus" & 
           Play != "Henry VIII" & 
           Play != "Much Ado about nothing") %>%
  arrange(desc(n)) %>%
  ggplot(., aes(x=bigramFac,y=n)) +
    geom_bar(aes(fill=gender),stat="identity") +
    scale_y_log10() +
    coord_flip() +
    facet_wrap(~Play,nrow=3)

5.3 Networks

#install.packages("igraph")
#install.packages("ggraph")
library(igraph)

Attaching package: ‘igraph’

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(ggraph)
library(grid)
a <- grid::arrow(type = "closed", angle=22.5, length = unit(.1, "inches"))

I think the most exciting way to use n-grams is probably network graphs. These graphs show what words co-occur, and in what order. Moreover, they can encode a number of dimensions visually, which would be very difficult to calculate by hand or plot in a more standard quantitative method.

5.3.1 Bigrams

From the list of bigrams we can generate from our corpus, we can plot a network graph in which the shade of the connection between nodes indicates the frequency of the bigram (darker means more frequent). Moreover, these connections (“edges”) are directional, so we can see which order the words are occuring in.

shak %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 22) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()

We can also use these plots to compare across plays (although token frequency begins to drop precipitously). Here, we can see that Twelfth Night has notable connections between items of clothing (yellow stockings, cross gartered), whereas Hamlet and Romeo and Juliet have more tokens referring to people and their stage directions. Hamlet additionally has a notable number of “father’s death” bigrams, while Romeo and Juliet mentions “county Paris” frequently.

p1 <- shak %>%
  filter(Play=="Hamlet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()
p2 <- shak %>%
  filter(Play == "Twelfth Night") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkred", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "salmon", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()
p3 <- shak %>%
  filter(Play == "Romeo and Juliet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkgreen", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "green2", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()
multiplot(p1,p2,p3,cols=3)

If we exclude stage directions and compare across six plays, we start to see distinct themes appear.

Using multiplot, try plotting the following six graphs in a 3 by 2 matrix:

  1. Hamlet
  2. Twelfth Night
  3. Romeo and Juliet
  4. Othello
  5. Henry IV
  6. The Tempest

Make sure each meets the following conditions:

  • no stage directions
  • bigrams with frequency greater than 3
  • each with a different colour

Remember: reusing code and adapting someone else’s (open source) code is encouraged! Always cite where appropriate.

# 1. Hamlet

# 2. Twelfth Night

# 3. Romeo and Juliet

# 4. Othello

# 5. Henry IV

# 6. The Tempest

(possible solution below)

























p1 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play=="Hamlet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Hamlet")
p2 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Twelfth Night") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkred", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "salmon", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Twelfth Night")
p3 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Romeo and Juliet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkgreen", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "green2", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Romeo and Juliet")
p4 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Othello") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkorange", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "orange", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Othello")
p5 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Henry IV") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="cadetblue4", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "cyan", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Henry IV")
p6 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "The Tempest") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="violet", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "magenta", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("The Tempest")
multiplot(p1,p2,p3,p4,p5,p6,cols=3)

6 Heatmaps

Another visualisation tool that can be very helpful for showing distributions of events across a structure is the heatmap. A heatmap is a complex histogram, which allows multiple subsets of the data to be compared side-to-side. In this example, we can look at the variability of length of subsections (acts and scenes) as determined by number of words. It is much easier and quicker to extract this information from a heatmap (although the precise numbers are lost in this version).

Let’s focus in on a subset of plays in order to simplify the visualisation. From a simple visual inspection, it appears that Act V tends to be the lightest on words, with the very notable exception of Love’s Labour’s Lost. Otherwise, Act I is generally fairly heavy on words, and Act II tends to be a bit lighter. Armed with that (superficial) observation, we might be able to check whether our eyes deceive us or whether there’s something to it. (But I’ll leave that exploration for another time.)

shak %>%
  filter(Play == "Hamlet" | Play == "King John" | 
           Play == "The Tempest" | Play == "Cymbeline" | 
           Play == "Measure for measure" | Play == "Timon of Athens" | 
           Play == "Richard III" | Play == "Loves Labours Lost" | 
           Play == "A Winters Tale" | Play == "Othello" | 
           Play == "Romeo and Juliet" | Play == "Henry V") %>% 
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Play,act, sort=TRUE) %>%
  transmute(play=Play, act=as.integer(act), n=n) %>%
  ggplot(aes(x=act,y=reorder(play, n))) + 
    geom_tile(aes(fill = n), colour = "white") + scale_fill_gradient(low = "white", high = "steelblue")

Maybe there is some pattern we can detect by plotting the number of words by scene, by act, and by player within a single play (Hamlet). This figure is able to communicate a tremendous amount of information in a very small space by illustrating where each character has the bulk of their lines. Here, we can see Hamlet is positively verbose in Scene II in Acts II, III, V, but otherwise only marginally wordier than the other players. Moreover, there seems to be the most even distribution of lines (neither very dark nor entirely pale, as colour-coded) in Act I, whereas the other acts are quite skewed toward the few main players.

shak %>%
  filter(Play == "Hamlet") %>% 
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Player,act,scene, sort=TRUE) %>%
  transmute(player=Player, act=as.integer(act), scene=as.integer(scene), n=n) %>%
  ggplot(aes(x=scene,y=reorder(player,n))) + 
    geom_tile(aes(fill = n), colour = "white") + 
    scale_fill_gradient(low = "white", high = "red2") +
    scale_x_continuous(breaks=c(0:8)) +
    theme_dark() + 
    facet_wrap(~act, ncol = 5)

Now, choose a different play and create your own version of this kind of graph. What can you learn about the contribution of each of the players from the distribution of their lines?

LS0tCnRpdGxlOiAiU2Vzc2lvbiAzOiBUZXh0IGFuZCBjb3JwdXMgZGF0YSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgICBkZl9wcmludDogcGFnZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCi0tLQoKVGhlIHB1cnBvc2Ugb2YgdGhpcyBub3RlYm9vayBpcyB0byBkZW1vbnN0cmF0ZSBzb21lIG9mIHdoYXQgaXMgcG9zc2libGUgZm9yIHRoZSBtaW5pbmcgYW5kIHZpc3VhbGlzYXRpb24gb2YgYSB0ZXh0LiBXZSdsbCBnbyB0aHJvdWdoIHNvbWUgb2YgdGhlIGJhc2ljIHRvb2xzIGFuZCBkbyBzb21lIGRhdGEgdmlzdWFsaXNhdGlvbiB0byBkZW1vbnN0cmF0ZSB3aGF0IGlzIHBvc3NpYmxlLiBJZiB0aGlzIGtpbmQgb2YgYW5hbHlzaXMgaXMgbm90IGRpcmVjdGx5IHJlbGV2YW50IHRvIHlvdXIgcmVzZWFyY2gsIGhvcGVmdWxseSB5b3UgY2FuIHVzZSB0aGlzIHRpbWUgdG8gcHJhY3RpY2UgZGlmZmVyZW50IGRhdGEgdmlzdWFsaXNhdGlvbiB0ZWNobmlxdWVzIGFuZCBiZWNvbWUgbW9yZSBmYW1pbGlhciB3aXRoIHNlZWtpbmcgb3V0IGFuc3dlcnMgdG8gcXVlc3Rpb25zIGFib3V0IHByb2dyYW1taW5nIFIgY29kZS4KCiMgU2V0dGluZyB1cAoKSW4gdGhpcyBzZXNzaW9uLCB3ZSdsbCBiZSB1c2luZyBhIGZldyBwYWNrYWdlcywgYnV0IHRoZXknbGwgYmUgaW50cm9kdWNlZCBhcyB3ZSBnby4gRmlyc3RseSwgd2UnbGwgbmVlZCB0byBsb2FkIGluIGB0aWR5dmVyc2VgIGFuZCBbYHRtYF0oaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tLykgKHRleHQgbWluaW5nKS4gV2Ugd2lsbCBub3QgZXhwbGljaXRseSBiZSB1c2luZyBbYFNub3diYWxsQ2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9Tbm93YmFsbEMvaW5kZXguaHRtbCkgYXQgdGhpcyB0aW1lLCBidXQgaXQgbWF5IGJlIHVzZWZ1bCB0byBrbm93IGFib3V0IGFzIGl0IGhlbHBzIHdpdGggbGVtbWF0aXphdGlvbiBmb3Igd29yZCBmcmVxdWVuY3kgYW5hbHlzaXMgaW4gc2V2ZXJhbCBsYW5ndWFnZXMuIEl0IGlzIG9uZSBvZiBgdG1gJ3MgZGVwZW5kZW5jaWVzLCBhbmQgbWF5IGJlIGRvaW5nIGEgYml0IG9mIGJhY2tncm91bmQgd29yay4gIAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBSIG11c3QgYmUgYXQgbGVhc3QgMy4zLjEgZm9yIGB0bWAgYW5kIGBzbGFtYCB0byB3b3JrLgojIGxpYnJhcnkoIlNub3diYWxsQyIpCmxpYnJhcnkodG0pCgpgYGAKCkJlZm9yZSB3ZSBnZXQgc3RhcnRlZCB3b3JraW5nIHdpdGggdGhlIGFjdHVhbCBkYXRhLCB3ZSdyZSBnb2luZyB0byBjcmVhdGUgYSBjdXN0b20gZnVuY3Rpb24gZm9yIHBsb3R0aW5nIG11bHRpcGxlIGBnZ3Bsb3RgIGdyYXBocyBpbiBhIHNpbmdsZSBpbWFnZS4gVGhpcyBmdW5jdGlvbiB3YXMgY29waWVkIGZyb20gW0Nvb2tib29rIGZvciBSXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9NdWx0aXBsZV9ncmFwaHNfb25fb25lX3BhZ2VfKGdncGxvdDIpLykuCgpgYGB7cn0KIyBNdWx0aXBsZSBwbG90IGZ1bmN0aW9uCiMKIyBnZ3Bsb3Qgb2JqZWN0cyBjYW4gYmUgcGFzc2VkIGluIC4uLiwgb3IgdG8gcGxvdGxpc3QgKGFzIGEgbGlzdCBvZiBnZ3Bsb3Qgb2JqZWN0cykKIyAtIGNvbHM6ICAgTnVtYmVyIG9mIGNvbHVtbnMgaW4gbGF5b3V0CiMgLSBsYXlvdXQ6IEEgbWF0cml4IHNwZWNpZnlpbmcgdGhlIGxheW91dC4gSWYgcHJlc2VudCwgJ2NvbHMnIGlzIGlnbm9yZWQuCiMKIyBJZiB0aGUgbGF5b3V0IGlzIHNvbWV0aGluZyBsaWtlIG1hdHJpeChjKDEsMiwzLDMpLCBucm93PTIsIGJ5cm93PVRSVUUpLAojIHRoZW4gcGxvdCAxIHdpbGwgZ28gaW4gdGhlIHVwcGVyIGxlZnQsIDIgd2lsbCBnbyBpbiB0aGUgdXBwZXIgcmlnaHQsIGFuZAojIDMgd2lsbCBnbyBhbGwgdGhlIHdheSBhY3Jvc3MgdGhlIGJvdHRvbS4KIwptdWx0aXBsb3QgPC0gZnVuY3Rpb24oLi4uLCBwbG90bGlzdD1OVUxMLCBmaWxlLCBjb2xzPTEsIGxheW91dD1OVUxMKSB7CiAgbGlicmFyeShncmlkKQoKICAjIE1ha2UgYSBsaXN0IGZyb20gdGhlIC4uLiBhcmd1bWVudHMgYW5kIHBsb3RsaXN0CiAgcGxvdHMgPC0gYyhsaXN0KC4uLiksIHBsb3RsaXN0KQoKICBudW1QbG90cyA9IGxlbmd0aChwbG90cykKCiAgIyBJZiBsYXlvdXQgaXMgTlVMTCwgdGhlbiB1c2UgJ2NvbHMnIHRvIGRldGVybWluZSBsYXlvdXQKICBpZiAoaXMubnVsbChsYXlvdXQpKSB7CiAgICAjIE1ha2UgdGhlIHBhbmVsCiAgICAjIG5jb2w6IE51bWJlciBvZiBjb2x1bW5zIG9mIHBsb3RzCiAgICAjIG5yb3c6IE51bWJlciBvZiByb3dzIG5lZWRlZCwgY2FsY3VsYXRlZCBmcm9tICMgb2YgY29scwogICAgbGF5b3V0IDwtIG1hdHJpeChzZXEoMSwgY29scyAqIGNlaWxpbmcobnVtUGxvdHMvY29scykpLAogICAgICAgICAgICAgICAgICAgIG5jb2wgPSBjb2xzLCBucm93ID0gY2VpbGluZyhudW1QbG90cy9jb2xzKSkKICB9CgogaWYgKG51bVBsb3RzPT0xKSB7CiAgICBwcmludChwbG90c1tbMV1dKQoKICB9IGVsc2UgewogICAgIyBTZXQgdXAgdGhlIHBhZ2UKICAgIGdyaWQubmV3cGFnZSgpCiAgICBwdXNoVmlld3BvcnQodmlld3BvcnQobGF5b3V0ID0gZ3JpZC5sYXlvdXQobnJvdyhsYXlvdXQpLCBuY29sKGxheW91dCkpKSkKCiAgICAjIE1ha2UgZWFjaCBwbG90LCBpbiB0aGUgY29ycmVjdCBsb2NhdGlvbgogICAgZm9yIChpIGluIDE6bnVtUGxvdHMpIHsKICAgICAgIyBHZXQgdGhlIGksaiBtYXRyaXggcG9zaXRpb25zIG9mIHRoZSByZWdpb25zIHRoYXQgY29udGFpbiB0aGlzIHN1YnBsb3QKICAgICAgbWF0Y2hpZHggPC0gYXMuZGF0YS5mcmFtZSh3aGljaChsYXlvdXQgPT0gaSwgYXJyLmluZCA9IFRSVUUpKQoKICAgICAgcHJpbnQocGxvdHNbW2ldXSwgdnAgPSB2aWV3cG9ydChsYXlvdXQucG9zLnJvdyA9IG1hdGNoaWR4JHJvdywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQucG9zLmNvbCA9IG1hdGNoaWR4JGNvbCkpCiAgICB9CiAgfQp9CmBgYAoKIyBSZWFkaW5nIGluIGNvcnB1cwoKRm9yIHRoaXMgZGVtb25zdHJhdGlvbiwgSSd2ZSBzZWxlY3RlZCBhIGNvcnB1cyBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzIGFuZCBhZGFwdGVkIHNvbWUgY29kZSBmcm9tIFthIEthZ2dsZSBub3RlYm9va10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zaW5kaHVlZS9sb3ZlLWluLXNoYWtlc3BlYXJlP3NjcmlwdFZlcnNpb25JZD0xMTIxMjcwKS4gT25jZSB5b3UgdW5kZXJzdGFuZCB3aGF0IHRoZSBjb2RlIGhlcmUgaXMgZG9pbmcsIGl0IGNhbiBlYXNpbHkgYmUgYWRhcHRlZCB0byBhIGNvcnB1cyBvZiBhbnkgc3RydWN0dXJlIG9yIGNvbnRlbnQuCgpXZSBzaG91bGQgdGFrZSBhIGxvb2sgYXQgaG93IHRoZSBjb3JwdXMgaXMgc3RydWN0dXJlZCBzbyB3ZSBrbm93IHdoYXQgd2UncmUgZGVhbGluZyB3aXRoLgoKYGBge3J9CnNoYWsgPC0gcmVhZC5jc3YoIi4uL2RhdGEvU2hha2VzcGVhcmVfZGF0YS5jc3YiLGhlYWRlciA9IFRSVUUsIGFzLmlzID0gVFJVRSkKYGBgCgpUaGVzZSBhcmUgdGhlIGZpcnN0IHRlbiBsaW5lcyBvZiB0aGUgY29ycHVzLiAgCgpgYGB7cn0Kc2hhawpgYGAKCkVhY2ggY29sdW1uIGlzIGxhYmVsZWQgYW5kIHRoZSBjb250ZW50IG9mIHRoZSBjb2x1bW4gaXMgY29uc2lzdGVudCBmb3IgZWFjaCByb3cgKGFsbCAxMTEzOTYgb2YgdGhlbSEpLiBTb21lIG9mIHRoZSByb3dzIG1heSBub3QgYmUgdXNlZnVsLiBTb21lIGNvbnRhaW4gZW1wdHkgY2VsbHMgKGxhYmVsZWQgYE5BYCkuIFNvbWUgY29udGFpbiBhIGxvdCBvZiBpbmZvcm1hdGlvbiBhbmQgd2UgbWlnaHQgbmVlZCB0byBkbyBzb21lIHByb2Nlc3Npbmcgb24gdGhlbSBiZWZvcmUgd2UgY2FuIHVzZSB0aGUgaW5mb3JtYXRpb24gcXVhbnRpdGF0aXZlbHkgKGZvciBleGFtcGxlIHRoZSBjb2x1bW4gYFBsYXllckxpbmVgKS4KCiMgV29yZCBmcmVxdWVuY3kKClRoZSBmaXJzdCB0aGluZyB3ZSdsbCBsb29rIGF0IGlzIHdvcmQgZnJlcXVlbmN5LCBvciBob3cgb2Z0ZW4gYSBzdHJpbmcgKGluIHRoaXMgY2FzZSAibG92ZSIpIG9jY3VycyBpbiB0aGUgZGF0YSBmcmFtZS4gVG8gZG8gdGhpcywgd2UgbXVzdCBpZGVudGlmeSBldmVyeSB0aW1lIHRoZSB3b3JkICJsb3ZlIiBhcHBlYXJzIGFuZCBoaWdobGlnaHQgaXQgaW4gYSB3YXkgc28gdGhhdCBpdCBjYW4gYmUgY291bnRlZCBiYXNlZCBvbiBkaWZmZXJlbnQgcHJvcGVydGllcyBvZiBpdHMgZW52aXJvbm1lbnQgKGUuZy4sIGJ5IHBsYXksIGJ5IHBsYXllciwgYnkgc2NlbmUsIGV0YykuCgpIZXJlIGFyZSB0aGUgZmlyc3QgMTAgcm93cyBvZiBhIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB0aGUgbnVtYmVyIG9mIHRpbWVzICJsb3ZlIiBhcHBlYXJzIGluIGVhY2ggcGxheS4gSXQncyBiZWVuIHNvcnRlZCBpbiBkZXNjZW5kaW5nIG9yZGVyLCBidXQgZG9lc24ndCBjb250YWluIGFueSBvdGhlciBpbmZvcm1hdGlvbiBhYm91dCB3aGVyZSBhbmQgd2hlbiB0aGUgd29yZCBvY2N1cnMuCgpgYGB7cn0KIyBwbGF5IGxldmVsIHdvcmQgZnJlcXVlbmN5CnBsYXlzIDwtIHVuaXF1ZShzaGFrJFBsYXkpCmxvdmVGcmVxPC1udW1lcmljKCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbGF5cykpewogICAgdGV4dCA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHBhc3RlKHNoYWtbc2hhayRQbGF5PT1wbGF5c1tpXSxdJFBsYXllckxpbmUsY29sbGFwc2U9IiAiKSkpCiAgICB0ZXh0IDwtIHRtX21hcCh0ZXh0LCByZW1vdmVQdW5jdHVhdGlvbikKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsIFBsYWluVGV4dERvY3VtZW50KQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQogICAgCiAgICAjIHN0ZW1taW5nIHRvIG1lcmdlIGFsbCAibG92ZWQiLCAibG92aW5nIiBpbnRvIG9uZSAgIAogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgc3RlbURvY3VtZW50KQogICAgdGRtICA8LSBUZXJtRG9jdW1lbnRNYXRyaXgodGV4dCkKICAgIAogICAgbG92ZUZyZXFbaV08LWFzLm51bWVyaWMoc2xhbTo6cm93X3N1bXModGRtKVsibG92ZSJdKSAjIGBzbGFtYCBtdXN0IGJlIGluc3RhbGxlZCBmb3IgdGhpcyB0byB3b3JrCiAgfQoKbFBsYXkgPC0gZGF0YS5mcmFtZShwbGF5cyxsb3ZlRnJlcSkKbFBsYXkgPC0gbmEub21pdChsUGxheSkKCiMgb3JkZXIgdGhlIHBsYXlzIGJhc2VkIG9uIHRoZSBvY2N1cmVuY2Ugb2YgbG92ZQpsUGxheTwtbFBsYXlbb3JkZXIoLWxQbGF5JGxvdmVGcmVxKSxdCgpsUGxheQpgYGAKCldlIGNhbiBhbHNvIGxvb2sgYXQgd2hpY2ggcGxheWVycyBzYXkgImxvdmUiIHRoZSBtb3N0IG92ZXIgdGhlIGNvdXJzZSBvZiB0aGVpciBhcHBlYXJlbmNlcy4gVGhlc2UgYXJlIG9ubHkgdGhlIHRvcCAxMCBwbGF5ZXJzIHdobyB1c2UgdGhlIHdvcmQgImxvdmUiIG1vc3QuIAoKYGBge3J9CiMgcGxheWVyIGxldmVsIHdvcmQgZnJlcXVlbmN5CnBsYXllcnMgPC0gdW5pcXVlKHNoYWskUGxheWVyKQpsb3ZlRnJlcSA8LSBudW1lcmljKCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbGF5ZXJzKSl7CiAgICB0ZXh0IDwtIENvcnB1cyhWZWN0b3JTb3VyY2UocGFzdGUoc2hha1tzaGFrJFBsYXllcj09cGxheWVyc1tpXSxdJFBsYXllckxpbmUsY29sbGFwc2U9IiAiKSkpCiAgICB0ZXh0IDwtIHRtX21hcCh0ZXh0LCByZW1vdmVQdW5jdHVhdGlvbikKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsIFBsYWluVGV4dERvY3VtZW50KQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgc3RlbURvY3VtZW50KQogICAgCiAgICB0ZG0gIDwtIFRlcm1Eb2N1bWVudE1hdHJpeCh0ZXh0KQogICAgCiAgICBsb3ZlRnJlcVtpXSA8LSBhcy5udW1lcmljKHNsYW06OnJvd19zdW1zKHRkbSlbImxvdmUiXSkKICB9CgpsUGxheWVyIDwtIGRhdGEuZnJhbWUocGxheWVycyxsb3ZlRnJlcSkKbFBsYXllciA8LSBuYS5vbWl0KGxQbGF5ZXIpCiNvcmRlcgpsUGxheWVyIDwtIGxQbGF5ZXJbb3JkZXIoLWxQbGF5ZXIkbG92ZUZyZXEpLF0KCmxQbGF5ZXIKYGBgCgojIyBFeHBsb3Jpbmcgc29tZW9uZSBlbHNlJ3MgY29kZQoKTGV0J3MgZ28gdGhyb3VnaCB0aGUgZm9yLWxvb3AgaW4gbW9yZSBkZXRhaWwgdG8gdW5kZXJzdGFuZCB3aGF0IGl0J3MgZG9pbmcuCgoqKkhvdyBtYW55IHBsYXllcnMgYXJlIGluIHRoaXMgdmVjdG9yPyoqIChBbnN3ZXI6IGByIGxlbmd0aChwbGF5ZXJzKWApICAKVGhhdCdzIHRoZSBudW1iZXIgb2YgdGltZXMgdGhpcyBmb3ItbG9vcCB3aWxsIGN5Y2xlLCBlYWNoIHRpbWUgaW5jcmVtZW50aW5nIHRoZSBsb29wIG51bWJlciBieSAxLgpgYGAKZm9yIChpIGluIDE6bGVuZ3RoKHBsYXllcnMpKXsKYGBgCgpUaGUgZmlyc3QgbGluZSBvZiB0aGUgbG9vcCB1c2VzIGBDb3JwdXMoKWAgYW5kIGBWZWN0b3JTb3VyY2UoKWAuICoqV2hhdCBkbyB0aGVzZSBmdW5jdGlvbnMgZG8/KiogIApXZSBjYW4gY2hlY2sgdGhlIEhlbHAgZmlsZXMgYnkgdHlwaW5nIGA/VmVjdG9yU291cmNlKClgLiBJdCBsb29rcyBsaWtlIGJvdGggZGVhbCB3aXRoIHRoZSBzdHJ1Y3R1cmUgYW5kIG1ldGFkYXRhIG9mIGEgY29ycHVzLiBJbXBvcnRhbnRseSwgd2UgY2FuIHNlZSBgW2ldYCBpbiB0aGUgY29kZSB0aG91Z2guIFRoaXMgaXMgdGhlIHNhbWUgYGlgIGZyb20gdGhlIHByZXZpb3VzIGxpbmUsIHdoaWNoIGN5Y2xlcyB0aHJvdWdoIGVhY2ggb2YgdGhlIHBsYXllcnMgaW4gdGhlIHZlY3Rvci4gIAoqKldoYXQgZG9lcyB0aGF0IHRlbGwgdXMgYWJvdXQgdGhlIGZpcnN0IHBhc3MgdGhyb3VnaCB0aGlzIGxvb3A/KioKYGBgCnRleHQgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShwYXN0ZShzaGFrW3NoYWskUGxheWVyPT1wbGF5ZXJzW2ldLF0kUGxheWVyTGluZSxjb2xsYXBzZT0iICIpKSkKYGBgCgpUaGUgbmV4dCBmZXcgbGluZXMgYWxsIHVzZSB0aGUgZnVuY3Rpb24gYHRtX21hcCgpYCwgd2hpY2ggcGVyZm9ybXMgYSB0cmFuc2Zvcm1hdGlvbiBvbiB0aGUgY29ycHVzLiBJbiB0aGlzIGNhc2UsIHRoZSBjb3JwdXMgaXMgdGhlIHN1YnNldCB3ZSd2ZSBjYWxsZWQgYHRleHRgIGluIHRoZSBhYm92ZSBsaW5lLiBKdWRnaW5nIGZyb20gdGhlIHN5bnRheCwgaXQgYXBwZWFycyB0aGF0IHRoZXkgcmVzcGVjdGl2ZWx5IHJlbW92ZSBwdW5jdHVhdGlvbiBmcm9tIHRoZSB0ZXh0LCBjb252ZXJ0IGl0IHRvIGEgcGxhaW4gdGV4dCBkb2N1bWVudCwgcmVtb3ZlIEVuZ2xpc2ggc3RvcCB3b3JkcywgYW5kIG1lcmdlIGFsbCB3b3JkcyB3aXRoIHRoZSBzYW1lIHN0ZW1zIG9yIGxlbW1hcyAoaS5lLiwgImxvdmUiLCAibG92aW5nIiwgImxvdmVkIiwgZXRjKS4KYGBgCnRleHQgPC0gdG1fbWFwKHRleHQsIHJlbW92ZVB1bmN0dWF0aW9uKQp0ZXh0IDwtIHRtX21hcCh0ZXh0LCBQbGFpblRleHREb2N1bWVudCkKdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQp0ZXh0IDwtIHRtX21hcCh0ZXh0LCBzdGVtRG9jdW1lbnQpCmBgYAoKVGhpcyBsaW5lIHR1cm5zIHRoZSByZXN1bHRpbmcgdmVjdG9yIGludG8gYSB0ZXJtLWRvY3VtZW50IG1hdHJpeCwgd2hpY2ggaXMgYmFzaWNhbGx5IGEgd2F5IG9mIGlkZW50aWZ5aW5nIHdoZXJlIGVhY2ggd29yZCBvY2N1cnMgbnVtZXJpY2FsbHksIHdoaWNoIHdpbGwgYWxsb3cgdXMgdG8gKmNvdW50KiB0aGUgd29yZHMgaW4gdGhlIG5leHQgbGluZS4gCmBgYAp0ZG0gIDwtIFRlcm1Eb2N1bWVudE1hdHJpeCh0ZXh0KQpgYGAKQSBzaW1wbGUgVERNIG1heSBsb29rIGxpa2UgdGhpczoKCgsgfCBlbnRlciB8IGV4aXQgfCBoYW1sZXQgfCBvcGhlbGlhIHwgYW5kCi0tfC0tfC0tfC0tfC0tfC0tCnRleHRfMSB8IDEgfCAwIHwgMSB8IDAgfCAwCnRleHRfMiB8IDEgfCAwIHwgMCB8IDEgfCAwCnRleHRfMyB8IDAgfCAxIHwgMSB8IDEgfCAxCgoqKldoYXQgbWlnaHQgdGhlc2UgdGhyZWUgInRleHRzIiBzYXk/KioKClRoZSBsYXN0IGxpbmUgaGFzIHNldmVyYWwgdGhpbmdzIGdvaW5nIG9uLiBXZSBjYW4gZmlndXJlIG91dCB3aGF0IGVhY2ggZnVuY3Rpb24gZG9lcyBieSB0cnlpbmcgaXQgb24gaXRzIG93bi4KYGBgCmxvdmVGcmVxW2ldIDwtIGFzLm51bWVyaWMoc2xhbTo6cm93X3N1bXModGRtKVsibG92ZSJdKQpgYGAKRmlyc3QsIHdlIGNhbiBzZWUgd2hhdCBgdGRtYCBsb29rcyBsaWtlIHdoZW4gYGkgPSAxYDoKYGBge3J9CnRkbQpgYGAKCkhtbW0sIHRoYXQgZG9lc24ndCBsb29rIGludGVycHJldGFibGUgYnkgYSBodW1hbi4gTWF5YmUgYHJvd19zdW1zKClgIGZyb20gdGhlIHBhY2thZ2UgYHNsYW1gIHdpbGwgaGVscD8KYGBge3J9CnNsYW06OnJvd19zdW1zKHRkbSkKYGBgCgpBaCBoYSEgVGhhdCBsb29rcyBtb3JlIGludGVsbGlnaWJsZS4gKipXaGljaCBwbGF5ZXIgaXMgdGhpcyBmb3I/KiogIAooQW5zd2VyID0gYHIgcGxheWVyc1sxXWA7IGlmIHlvdSB3YW50IHRvIHNlZSB3aGF0IGRpZmZlcmVudCBwbGF5ZXJzIGFyZSBkb2luZywgeW91IGNhbiBjaGFuZ2UgdGhlIHZhbHVlIG9mIGBpYCBieSBoYW5kIGFuZCByZS1ydW4gdGhlIGNvZGUuKQoKU28gd2hhdCBpcyBgWyJsb3ZlIl1gIGRvaW5nIGluIHRoZSBjb2RlPwpgYGB7cn0Kc2xhbTo6cm93X3N1bXModGRtKVsibG92ZSJdCmBgYAoKQWgsICJsb3ZlIiBkb2Vzbid0IGV4aXN0IGluIHRoaXMgcm93ICh3aGVyZSBgaSA9IDFgKS4gTGV0J3Mgc2VlIHdoYXQgaXQgd291bGQgbG9vayBsaWtlIGlmIHdlIHdlcmUgYWN0dWFsbHkgaW50ZXJlc3RlZCBpbiAiZW50ZXIiOgpgYGB7cn0Kc2xhbTo6cm93X3N1bXModGRtKVsiZW50ZXIiXQpgYGAKClRoZXJlIGFyZSB0d28gZWxlbWVudHMgdGhhdCBhcmUgYmVpbmcgcHJpbnRlZCBvdXQgaGVyZTogdGhlIHdvcmQgKCJlbnRlciIpIGFuZCB0aGUgc3VtICgzKS4gVGhhdCdzIHdoYXQgYGFzLm51bWVyaWMoKWAgd2lsbCBoZWxwIHVzIHdpdGg6CmBgYHtyfQphcy5udW1lcmljKHNsYW06OnJvd19zdW1zKHRkbSlbImVudGVyIl0pCmBgYAoKVGhpcyBtZWFucyB0aGF0IHRoZSBzdW0gb2Ygb2NjdXJyZW5jZXMgb2YgImxvdmUiIGlzIG5vdyBlbnRlcmVkIGludG8gaW5kZXggYFtpXWAgaW4gdGhlIHZlY3RvciBgbG92ZUZyZXFgLCBhbmQgdGhlIGxvb3AgYmVnaW5zIGFnYWluIHVudGlsIGBpID0gbGVuZ3RoKHBsYXllcnMpYC4KCkZpbmFsbHksIHdlIGNsb3NlIG9mZiB0aGUgZm9yLWxvb3AuCmBgYAp9CmBgYAoKQ2FuIHlvdSBmaWd1cmUgb3V0IGhvdyB0byB1c2UgdGhlIGNvZGUgYWJvdmUgdG8gY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBmb2xsb3dpbmcgdHdvIGNvbmRpdGlvbnM/OgoKKiBBIGxpc3Qgb2YgKipwbGF5ZXJzKioKKiBPcmRlcmVkIGJ5IGZyZXF1ZW5jeSBvZiB0aGUgd29yZCAqKmRlYXRoKioKCmBgYHtyfQojIGNyZWF0ZSB0aGUgdmVjdG9ycyB5b3UgbmVlZAojIGNyZWF0ZSB0aGUgZm9yLWxvb3AgdG8gY2FsY3VsYXRlIGZyZXF1ZW5jeQojIGNyZWF0ZSB0aGUgZGF0YSBmcmFtZQojIG9yZGVyIHRoZSBkYXRhIGZyYW1lIGZyb20gbW9zdCB0byBsZWFzdCBmcmVxdWVudApgYGAKCjxiaWc+KHBvc3NpYmxlIHNvbHV0aW9uIGJlbG93KTwvYmlnPiAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCgpgYGB7cn0KIyBjcmVhdGUgdGhlIHZlY3RvcnMgeW91IG5lZWQKIyBwbGF5ZXJzIDwtIHVuaXF1ZShzaGFrJFBsYXllcikgIyA8LS0gd2UgYWxyZWFkeSBtYWRlIHRoaXMgb25lIGJlZm9yZSwgYnV0IHdlIGNhbiBkbyBpdCBhZ2FpbiB0b28KZGVhdGhGcmVxIDwtIG51bWVyaWMoKSAjIDwtLSBjcmVhdGUgYSB2ZWN0b3Igb2YgY2xhc3MgIm51bWVyaWMiCgojIGNyZWF0ZSB0aGUgZm9yLWxvb3AgdG8gY2FsY3VsYXRlIGZyZXF1ZW5jeQpmb3IgKGkgaW4gMTpsZW5ndGgocGxheWVycykpewogICAgdGV4dCA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHBhc3RlKHNoYWtbc2hhayRQbGF5ZXI9PXBsYXllcnNbaV0sXSRQbGF5ZXJMaW5lLGNvbGxhcHNlPSIgIikpKQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlUHVuY3R1YXRpb24pCiAgICB0ZXh0IDwtIHRtX21hcCh0ZXh0LCBQbGFpblRleHREb2N1bWVudCkKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoJ2VuZ2xpc2gnKSkKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsc3RlbURvY3VtZW50KQogICAgCiAgICB0ZG0gIDwtIFRlcm1Eb2N1bWVudE1hdHJpeCh0ZXh0KQogICAgCiAgICBkZWF0aEZyZXFbaV0gPC0gYXMubnVtZXJpYyhzbGFtOjpyb3dfc3Vtcyh0ZG0pWyJkZWF0aCJdKQogIH0KCiMgY3JlYXRlIHRoZSBkYXRhIGZyYW1lCmRQbGF5ZXIgPC0gZGF0YS5mcmFtZShwbGF5ZXJzLGRlYXRoRnJlcSkKZFBsYXllciA8LSBuYS5vbWl0KGRQbGF5ZXIpCiMgb3JkZXIgdGhlIGRhdGEgZnJhbWUgZnJvbSBtb3N0IHRvIGxlYXN0IGZyZXF1ZW50CmRQbGF5ZXIgPC0gZFBsYXllcltvcmRlcigtZFBsYXllciRkZWF0aEZyZXEpLF0KZFBsYXllcgpgYGAKCiMgVmlzdWFsaXNpbmcgYSBjb3JwdXMKCk1heWJlIHlvdSBhcmUgY3VyaW91cyBob3cgdGhlIGxvbmdlciBhbmQgc2hvcnRlciBwbGF5cyBjb21wYXJlIGluIHdvcmQgZnJlcXVlbmN5LiBJbnN0ZWFkIG9mIGhhbmQtY291bnRpbmcgZWFjaCwgd2UgY2FuIGdyYXBoIGFuZCBvcmRlciB0aGVtLiBCYXNlZCBvbiB0aGlzIGdyYXBoLCB5b3UgZG9uJ3QgbmVlZCB0byBrbm93IGV4YWN0bHkgaG93IGxvbmcgZWFjaCBpcywgYnV0IHlvdSBjYW4gc2VlIHRoYXQgKk90aGVsbG8qIGlzIG11Y2ggbG9uZ2VyIHRoYW4gKkxvdmVzIExhYm91cnMgTG9zdCosIHdoaWNoIGNhbiBpbmZvcm0gaG93IHlvdSBhcHByb2FjaCB0aGUgY29tcGFyaXNvbiBsYXRlciBvbi4KCmBgYHtyfQpzaGFrICU+JQogICMgYFBsYXlgIGlzIHRoZSBmYWN0b3Igd2UgYXJlIGludGVyZXN0ZWQgaW4KICBncm91cF9ieShQbGF5KSAlPiUgCiAgIyBzdW1tYXJpc2UgdGhlIGNvbnRlbnQgb2YgYFBsYXlgIGJ5IGNvdW50aW5nIGhvdyBtYW55IGluc3RhbmNlcyB0aGVyZSBhcmUgb2YgZWFjaCAoYG5gKQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUgCiAgIyByZW9yZGVyKCkgYWxsb3dzIHVzIHRvIHNvcnQgdGhlbSBncmVhdGVzdCB0byBsZWFzdAogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKFBsYXksIG4pLHk9bikpICsgCiAgICAjIHN0YXQ9ImlkZW50aXR5IiBpcyBjcnVjaWFsIGZvciBnZ3Bsb3QgdG8ga25vdyBlYWNoIGBuYCBpcyB0aGUgbGVuZ3RoIG9mIHRoZSBiYXIKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKyAKICAgICMgdGhpcyB3YXksIHRoZSBwbGF5IG5hbWVzIHdpbGwgYmUgaG9yaXpvbnRhbCBhbmQgZnVsbHkgZGlzcGxheWVkCiAgICBjb29yZF9mbGlwKCkgKyAKICAgIGdndGl0bGUoIkxlbmd0aCBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzIikgKwogICAgeGxhYigiUGxheSIpICsKICAgIHlsYWIoIk51bWJlciBvZiBsaW5lcyIpCmBgYAoKIyMgUGVyY2VwdGlvbiBvZiBmcmVxdWVuY3kKCldpdGhpbiBhIHNpbmdsZSBwbGF5LCBtYXliZSB3ZSB3YW50IHRvIGtub3cgd2hpY2ggY2hhcmFjdGVycyBhcmUgdGhlIGNoYXR0aWVzdC4gV2UgY2FuIHZpc3VhbGlzZSB0aGUgbnVtYmVyIG9mIGxpbmVzIG9mIHRleHQgcGVyIGNoYXJhY3RlciB0byBnZXQgYSBzZW5zZSBvZiB3aG8gaXMgZG9taW5hdGluZyB0aGUgc3RhZ2UuCgpgYGB7cn0Kc2hhayAlPiUKICBmaWx0ZXIoUGxheSA9PSAiSGFtbGV0IikgJT4lICMgbGltaXQgb3VyIGRhdGFzZXQgdG8gb25seSB0aGUgcGxheSAiSGFtbGV0IgogIGdyb3VwX2J5KFBsYXllcikgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKFBsYXllciwgbikseT1uKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiU3BlZWNoIGluIEhhbWxldCIpICsKICAgIHhsYWIoIlBsYXllciIpICsKICAgIHlsYWIoIk51bWJlciBvZiBsaW5lcyIpICMrCiAgICAjc2NhbGVfeV9sb2cxMCgpCmBgYAoKV2UgY2FuIGFsc28gbG9vayBhY3Jvc3MgcGxheXMgZm9yIGZyZXF1ZW5jeS4gQnkgY29tcGFyaW5nIHdoaWNoIHBsYXlzIGhhdmUgdGhlIHdvcmQgImxvdmUiIHRoZSBtb3N0IG9mdGVuLCB3ZSBtaWdodCBiZSBhYmxlIHRvIGdyb3VwIHRoZW0gKHBlcmNlcHR1YWxseSkgaW50byBwbGF5cyAqYWJvdXQgbG92ZSogYW5kIHRob3NlIHRoYXQgYXJlIG5vdC4gTWF5YmU/CgpgYGB7cn0KbFBsYXkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIocGxheXMsIGxvdmVGcmVxKSx5PWxvdmVGcmVxKSkgKwogICAgZ2VvbV9iYXIoYWVzKCksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiTG92ZSBpbiBlYWNoIHBsYXkiKSArCiAgICB4bGFiKCJQbGF5IikgKwogICAgeWxhYigiZnJlcXVlbmN5IG9mIHRoZSB3b3JkICdsb3ZlJyIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyBDb21wYXJpbmcgYWNyb3NzIHN1YnNldHMKCk9uZSB0aGluZyB0aGF0IGdyYXBocyBjYW4gZG8gdmVyeSBlYXNpbHkgaXMgZ2l2ZSB5b3UgYSB3YXkgdG8gaWRlbnRpZnkgdHJlbmRzIHdoZW4geW91IHNvcnQgZXZlbnRzIChlLmcuLCBwbGF5cykgaW50byBtdWx0aXBsZSBkaWZmZXJlbnQgY2F0ZWdvcmllcy4gRm9yIGluc3RhbmNlLCB0aGUgZnJlcXVlbmN5IGdyYXBoIGFib3ZlIGlzIGludGVyZXN0aW5nLCBidXQgdGhlcmUgYXJlIHNvIG1hbnkgcGxheXMgYW5kIGFzIGEgbm9uLWV4cGVydCwgSSBjYW4ndCB0ZWxsIHlvdSB3aGF0IGVhY2ggaXMgYWJvdXQsIHdoYXQgc3R5bGUgaXQgaXMgd3JpdHRlbiBpbiwgb3Igd2hldGhlciBJJ2QgZXhwZWN0IGl0IHRvIGJlIGFib3V0ICJsb3ZlIiBvciBub3QuIFNvLCB3ZSBjYW4gYWRkIGFub3RoZXIgZGltZW5zaW9uIG9mIGluZm9ybWF0aW9uLgoKYGBge3J9CmxQbGF5Q2F0IDwtIGxQbGF5CgojIGNyZWF0ZSBib29sZWFuIHZlY3RvcnMgZm9yIGZvdXIgcGxheSBjYXRlZ29yaWVzCmNvbWVkaWVzIDwtIGMoIkEgQ29tZWR5IG9mIEVycm9ycyIsCiAgICAgICAgICAgICJBcyB5b3UgbGlrZSBpdCIsCiAgICAgICAgICAgICJBbGxzIHdlbGwgdGhhdCBlbmRzIHdlbGwiLAogICAgICAgICAgICAiTG92ZXMgTGFib3VycyBMb3N0IiwKICAgICAgICAgICAgIk1lYXN1cmUgZm9yIG1lYXN1cmUiLAogICAgICAgICAgICAiTWVyY2hhbnQgb2YgVmVuaWNlIiwKICAgICAgICAgICAgIk1lcnJ5IFdpdmVzIG9mIFdpbmRzb3IiLAogICAgICAgICAgICAiQSBNaWRzdW1tZXIgbmlnaHRzIGRyZWFtIiwKICAgICAgICAgICAgIk11Y2ggQWRvIGFib3V0IG5vdGhpbmciLAogICAgICAgICAgICAiVGFtaW5nIG9mIHRoZSBTaHJldyIsCiAgICAgICAgICAgICJUd2VsZnRoIE5pZ2h0IiwKICAgICAgICAgICAgIlR3byBHZW50bGVtZW4gb2YgVmVyb25hIikKcm9tYW5jZXMgPC0gYygiUGVyaWNsZXMiLAogICAgICAgICAgICAgIkN5bWJlbGluZSIsCiAgICAgICAgICAgICAiQSBXaW50ZXJzIFRhbGUiLAogICAgICAgICAgICAgIlRoZSBUZW1wZXN0IikKaGlzdG9yaWVzIDwtIGMoIktpbmcgSm9obiIsCiAgICAgICAgICAgICAiUmljaGFyZCBJSSIsCiAgICAgICAgICAgICAiUmljaGFyZCBJSUkiLAogICAgICAgICAgICAgIkhlbnJ5IElWIiwKICAgICAgICAgICAgICJIZW5yeSBWIiwKICAgICAgICAgICAgICJIZW5yeSBWSSBQYXJ0IDEiLAogICAgICAgICAgICAgIkhlbnJ5IFZJIFBhcnQgMiIsCiAgICAgICAgICAgICAiSGVucnkgVkkgUGFydCAzIiwKICAgICAgICAgICAgICJIZW5yeSBWSUlJIiwKICAgICAgICAgICAgICJDb3Jpb2xhbnVzIiwKICAgICAgICAgICAgICJKdWxpdXMgQ2Flc2FyIiwKICAgICAgICAgICAgICJBbnRvbnkgYW5kIENsZW9wYXRyYSIsCiAgICAgICAgICAgICAiS2luZyBMZWFyIiwKICAgICAgICAgICAgICJtYWNiZXRoIikKdHJhZ2VkaWVzIDwtIGMoIlRpdHVzIEFuZHJvbmljdXMiLAogICAgICAgICAgICAgIlJvbWVvIGFuZCBKdWxpZXQiLAogICAgICAgICAgICAgIkhhbWxldCIsCiAgICAgICAgICAgICAiVHJvaWx1cyBhbmQgQ3Jlc3NpZGEiLAogICAgICAgICAgICAgIk90aGVsbG8iLAogICAgICAgICAgICAgIlRpbW9uIG9mIEF0aGVucyIpCiMgY3JlYXRlIGJvb2xlYW4gdmVjdG9ycyBmb3IgZm91ciBwbGF5IGNhdGVnb3JpZXMKY29tZWR5IDwtIGxQbGF5Q2F0JHBsYXlzICVpbiUgY29tZWRpZXMKcm9tYW5jZSA8LSBsUGxheUNhdCRwbGF5cyAlaW4lIHJvbWFuY2VzCmhpc3RvcnkgPC0gbFBsYXlDYXQkcGxheXMgJWluJSBoaXN0b3JpZXMKdHJhZ2VkeSA8LSBsUGxheUNhdCRwbGF5cyAlaW4lIHRyYWdlZGllcwoKIyBjcmVhdGUgbmV3IGNvbHVtbiB3aXRoIHRoZXNlIGZvdXIgY2F0ZWdvcmllcwpsUGxheUNhdCA8LSBsUGxheSAlPiUKICBtdXRhdGUoY2F0ZWdvcnkgPSBjYXNlX3doZW4oY29tZWR5ID09IFRSVUUgfiAiY29tZWR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9tYW5jZSA9PSBUUlVFIH4gInJvbWFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaXN0b3J5ID09IFRSVUUgfiAiaGlzdG9yeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWdlZHkgPT0gVFJVRSB+ICJ0cmFnZWR5IikpCmBgYApgciBsUGxheUNhdGAKCkluIHRoZSBmb2xsb3dpbmcgZ3JhcGgsIGVhY2ggY29sb3IgcmVwcmVzZW50cyBhIGRpZmZlcmVudCBjYXRlZ29yeSAoYXMgZGV0ZXJtaW5lZCBieSBXaWtpcGVkaWEncyBbRmlyc3QgRm9saW9dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZpcnN0X0ZvbGlvKSBwYWdlLCBwbHVzIGluZm9ybWF0aW9uIGFib3V0IHRoZSAibGF0ZSByb21hbmNlcyIpLiBOb3csIHdlIGNhbiBzZWUgaWYgdGhlcmUgYXJlIHRyZW5kcyBmb3IgZGlmZmVyZW50IGNhdGVnb3JpZXMgdG8gbWVudGlvbiAibG92ZSIgbW9yZSBvciBsZXNzIHRoYW4gdGhlIG90aGVycy4KCmBgYHtyfQpsUGxheUNhdCAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cmVvcmRlcihwbGF5cywgbG92ZUZyZXEpLHk9bG92ZUZyZXEpKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD1jYXRlZ29yeSksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiTG92ZSBpbiBlYWNoIHBsYXkiKSArCiMgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogICAgeGxhYigiUGxheSIpICsKICAgIHlsYWIoImZyZXF1ZW5jeSBvZiB0aGUgd29yZCAnbG92ZSciKQpgYGAKCkl0IHNlZW1zIHRvIG1lIHRoYXQgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6I0Y4NzY2RCI+Y29tZWRpZXM8L3NwYW4+IGFuZCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojQzc3Q0ZGIj50cmFnZWRpZXM8L3NwYW4+IGRpc2N1c3MgImxvdmUiIHRoZSBtb3N0LCB3aGVyZWFzIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiM3Q0FFMDAiPmhpc3Rvcmllczwvc3Bhbj4gYW5kIHRoZSBsYXRlIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiMwMEJGQzQiPnJvbWFuY2VzPC9zcGFuPiBkaXNjdXNzIGl0IHRoZSBsZWFzdC4gSXMgdGhpcyBpbnR1aXRpdmU/IE1heWJlLiBCdXQgdGhlcmUncyBhIHByb2JsZW0uICpBIENvbWVkeSBvZiBFcnJvcnMqIGhhcyB0aGUgZmV3ZXN0IG1lbnRpb25zIG9mICJsb3ZlIiwgYnV0IGl0J3MgYWxzbyB0aGUgc2hvcnRlc3QgcGxheSwgc28gaXQgaGFzIHRoZSBmZXdlc3Qgd29yZHMgb3ZlcmFsbC4gV2hhdCB3ZSByZWFsbHkgd2FudCB0byBzZWUgaXMgdGhlIHByb3BvcnRpb24gb2YgImxvdmUiLWZyZXF1ZW5jeSBwZXIgcGxheSwgbm90IHRoZSByYXcgY291bnRzLiBUbyBkbyB0aGF0LCB3ZSBoYXZlIHRvIGFkZCBpbiB0aGUgdG90YWwgbGVuZ3RoIG9mIGVhY2ggcGxheSB0byB0aGUgZGF0YSBmcmFtZSwgYnV0IHdlJ3ZlIGxvc3QgdGhhdCBpbmZvcm1hdGlvbiBpbiBgbFBsYXlDYXRgIGFuZCBgbFBsYXlgLiAqKkhvdyBtaWdodCB5b3UgYWRkIHRoYXQgaW5mb3JtYXRpb24gaW4gdG8gdGhlIGBsUGxheUNhdGAgZGF0YWZyYW1lPyoqCgpgYGB7cn0KCmBgYAoKPGJpZz4ocG9zc2libGUgc29sdXRpb24gYmVsb3cpPC9iaWc+ICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoKCgpgYGB7cn0KIyBjcmVhdGUgbmV3IGRhdGEgZnJhbWUgZm9yIGxlbmd0aHMgb2YgcGxheXMKcGxheUxlbmd0aCA8LSBzaGFrICU+JQogIGdyb3VwX2J5KFBsYXkpICU+JQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUKICB0cmFuc211dGUocGxheXM9UGxheSxsZW5ndGg9bikgIyByZW5hbWUgY29sdW1ucyB0aGF0IHdlIHdhbnQsIHdoaWNoIGhhcHBlbnMgdG8gYmUgYm90aCBvZiB0aGVtCgojIGFkZCBjb2x1bW4gdG8gbFBsYXlDYXQKbFBsYXlDYXRMZW4gPC0gbFBsYXlDYXQgJT4lCiAgbGVmdF9qb2luKC4scGxheUxlbmd0aCkKYGBgCmByIGxQbGF5Q2F0TGVuYAoKTm93IHdlIGNhbiB0YWtlIGEgbG9vayB0byBzZWUgaG93IHJhdyBhbmQgcHJvcG9ydGlvbmFsIGdyYXBocyBjb21wYXJlLgoKYGBge3J9CmxQbGF5Q2F0TGVuICU+JQogIG11dGF0ZShwcm9wb3J0aW9uID0gbG92ZUZyZXEvbGVuZ3RoKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cmVvcmRlcihwbGF5cywgcHJvcG9ydGlvbikseT1wcm9wb3J0aW9uKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9Y2F0ZWdvcnkpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIkxvdmUgaW4gZWFjaCBwbGF5IikgKwojICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICAgIHhsYWIoIlBsYXkiKSArCiAgICB5bGFiKCJwcm9wb3J0aW9uYWwgZnJlcXVlbmN5IG9mIHRoZSB3b3JkICdsb3ZlJyIpCgpgYGAKCk5vdCBhIHdob2xlIGxvdCBoYXMgY2hhbmdlZCwgYnV0IEkgdGhpbmsgdGhlIGRpc3RyaWJ1dGlvbiBvZiA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojRjg3NjZEIj5jb21lZGllczwvc3Bhbj4gYW5kIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNDNzdDRkYiPnRyYWdlZGllczwvc3Bhbj4gaXMgZXZlbiBtb3JlIHByb25vdW5jZWQuIEFuZCwgd2UgaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0ICpBIENvbWVkeSBvZiBFcnJvcnMqLCB3aGljaCBpcyBzdGlsbCB2ZXJ5IGNsb3NlIHRvIHRoZSBib3R0b20gb2YgdGhlIGdyYXBoLiBOb3QgZXZlcnkgY29tZWR5IGlzIGFib3V0IGxvdmUsIGl0IHNlZW1zLgoKRmluYWxseSwgd2UgY2FuIGdlbmVyYXRlIHRoZXNlIHNhbWUgdHlwZXMgb2YgZ3JhcGhzIGZvciBkaWZmZXJlbnQgc3ViZ3JvdXBzLCB0b28uIFdlJ2xsIGhhdmUgdG8gYWRkIGluIGAkY2F0ZWdvcnlgIHRvIGBzaGFrYCBhcyB3ZWxsLCBidXQgd2UgYWxyZWFkeSBoYXZlIHRoZSB2ZWN0b3JzIGZvciBlYWNoIGxldmVsIG9mIGBjYXRlZ29yeWAsIHNvIGl0J3MgcHJldHR5IGVhc3kgdG8gZG8uCgpgYGB7cn0KIyBjcmVhdGUgYm9vbGVhbiB2ZWN0b3JzIGZvciBmb3VyIHBsYXkgY2F0ZWdvcmllcyBpbiBgc2hha2AKY29tZWR5ICA8LSBzaGFrJFBsYXkgJWluJSBjb21lZGllcwpyb21hbmNlIDwtIHNoYWskUGxheSAlaW4lIHJvbWFuY2VzCmhpc3RvcnkgPC0gc2hhayRQbGF5ICVpbiUgaGlzdG9yaWVzCnRyYWdlZHkgPC0gc2hhayRQbGF5ICVpbiUgdHJhZ2VkaWVzCgojIGNyZWF0ZSBuZXcgY29sdW1uIHdpdGggdGhlc2UgZm91ciBjYXRlZ29yaWVzCnNoYWtDYXQgPC0gc2hhayAlPiUKICBtdXRhdGUoY2F0ZWdvcnkgPSBjYXNlX3doZW4oY29tZWR5ID09IFRSVUUgfiAiY29tZWR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9tYW5jZSA9PSBUUlVFIH4gInJvbWFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaXN0b3J5ID09IFRSVUUgfiAiaGlzdG9yeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWdlZHkgPT0gVFJVRSB+ICJ0cmFnZWR5IikpCmBgYApgciBzaGFrQ2F0YAoKSGVyZSdzIG9uZSBleGFtcGxlLCB3aGVyZSB3ZSBsb29rIGF0IHRoZSBudW1iZXIgb2YgbGluZXMgZWFjaCBwbGF5ZXIgaGFzLCBmb2N1c2luZyBvbmx5IG9uIHBsYXllcnMgd2hvIGhhdmUgZ3JlYXRlciB0aGFuIDcwMCBsaW5lcy4gV2UgY2FuIGFsc28gc2VlIGlmIHRoZXJlIGFyZSBhbnkgdHJlbmRzIGluIHRoZXNlIHRvcCBzcGVha2VycyBieSBwbGF5IGNhdGVnb3J5LiBJdCBzZWVtcyB0byBtZSB0aGF0IHRoZSA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojN0NBRTAwIj5oaXN0b3JpZXM8L3NwYW4+IGRvbWluYXRlLCBidXQgSGFtbGV0IGFuZCBJYWdvIGRvbWluYXRlIHRoZSBzY2VuZSAoc28gdG8gc3BlYWspLgoKYGBge3J9CnNoYWtDYXQgJT4lCiAgZ3JvdXBfYnkoUGxheSxQbGF5ZXIsY2F0ZWdvcnkpICU+JQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUKICBmaWx0ZXIobiA+IDcwMCkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIoUGxheWVyLCBuKSx5PW4pKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD1jYXRlZ29yeSksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiTnVtYmVyIG9mIGxpbmVzIGJ5IGNoYXJhY3RlciIpICsKIyAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgICB4bGFiKCJQbGF5ZXIiKSArCiAgICB5bGFiKCJOdW1iZXIgb2YgbGluZXMiKQpgYGAKCklzIHRoaXMgYmVjYXVzZSA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojN0NBRTAwIj5oaXN0b3JpZXM8L3NwYW4+IGFuZCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojQzc3Q0ZGIj50cmFnZWRpZXM8L3NwYW4+IHRlbmQgdG8gYmUgbG9uZ2VyIHBsYXlzLCBvdmVyYWxsPyBRdWl0ZSBwb3NzaWJseToKCgpgYGB7cn0Kc2hha0NhdCAlPiUKICBncm91cF9ieShQbGF5LGNhdGVnb3J5KSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIoUGxheSwgbikseT1uKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9Y2F0ZWdvcnkpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIkxlbmd0aCBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzIikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogICAgeGxhYigiUGxheSIpICsKICAgIHlsYWIoIk51bWJlciBvZiBsaW5lcyIpCmBgYAoKIyBOLWdyYW1zCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoInRpZHl0ZXh0IikKbGlicmFyeSh0aWR5dGV4dCkKYGBgCgpTaW5nbGUgd29yZHMgbWlnaHQgbm90IGJlIGFibGUgdG8gdGVsbCB1cyBtdWNoIGFib3V0IHRoZSB0ZXh0cywgd2hpY2ggaXMgd2h5IGV4YW1pbmluZyBjb2xsb2NhdGlvbnMgaXMgc3VjaCBhIHBvcHVsYXIgdGVjaG5pcXVlLiBXaGF0J3MgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiAidGhlIGtpbmciLCAia2lsbCB0aGUga2luZyIsIGFuZCAia2lzcyB0aGUga2luZyI/IEEgbG90LCBidXQgd2Ugd29uJ3Qga25vdyBpZiB3ZSBvbmx5IGxvb2sgZm9yIGluc3RhbmNlcyBvZiAia2luZyIuIFRoZXJlIGlzIHdoZXJlICpuLWdyYW1zKiBiZWNvbWUgdXNlZnVsLiBOLWdyYW1zIGFyZSBzZXRzIG9mIGFkamFjZW50IHdvcmRzLCBjYWxjdWxhdGVkIGJ5IGFzc2lnbmluZyBhIG51bWJlciB0byAnbicuIFRoYXQgaXMsIGlmIHdlIHdhbnQgc2V0cyBvZiB0d28gd29yZHMsIHdlIHRhbGsgYWJvdXQgKmJpZ3JhbXMqLiBJZiB3ZSB3YW50IHNldHMgb2YgdGhyZWUgd29yZHMsIHdlIHRhbGsgYWJvdXQgKnRyaWdyYW1zKi4gCgojIyBQcmUtcHJvY2Vzc2luZyB0aGUgY29ycHVzCgpGaXJzdCwgd2UgY2FuIGxvb2sgYXQgdGhlIGNvcnB1cyBhcyBhIGxpc3Qgb2Ygd29yZHMsIHJhdGhlciB0aGFuIGEgbGlzdCBvZiBsaW5lcyAoYnkgYWN0IGFuZCBzY2VuZSkuICoqV2hhdCBpcyBgdW5uZXN0X3Rva2VucygpYCBkb2luZyBoZXJlPyoqCgpgYGB7cn0Kc2hhayAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gd29yZCkKYGBgCgpOb3csIHdlIGNhbiBhdXRvbWF0ZSB0aGUgcHJvY2VzcyBvZiBjb3VudGluZyBob3cgb2Z0ZW4gZWFjaCB3b3JkIG9jY3Vycy4gQnV0LCBvZiBjb3Vyc2UsIGNlcnRhaW4gd29yZHMgYXJlIGdvaW5nIHRvIGJlIGV4dHJlbWVseSBjb21tb24sIGFuZCB0aG9zZSB3b3JkcyBhcmUgdW5saWtlbHkgdG8gYmUgaW5mb3JtYXRpdmUuCgpgYGB7cn0Kc2hhayAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gd29yZCkgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCmBgYAoKT25jZSB3ZSd2ZSBmaWx0ZXJlZCBvdXQgb3VyICpzdG9wLXdvcmRzKiwgdGhlIG1vc3QgZnJlcXVlbnQgd29yZHMgbG9vayBxdWl0ZSBkaWZmZXJlbnQuCgpgYGB7cn0Kc2hhayAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gd29yZCkgJT4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgpIb3dldmVyLCBTaGFrZXNwZWFyZSB1c2VzIGEgbG90IG9mIHdvcmRzIHRoYXQgYXJlbid0IGluIG91ciBkZWZhdWx0IHN0b3Atd29yZCBsaXN0LCBzbyB3ZSBjYW4gYXBwZW5kIG91ciBvd24gY3VzdG9tIGxpc3QuCgpgYGB7cn0Kd29yZCA8LSBjKE5BLCJ0aG91IiwidGhlZSIsInRoeSIsInRoaW5lIiwiZG9zdCIsInNoYWx0Iiwid2lsdCIsImhhc3QiLCJoYXRoIiwic2NlbmUiLCJ0aXMiLCJpaSIsImlpaSIsIml2IiwidiIsInZpIiwidmlpIikKbGV4aWNvbiA8LSByZXAoInNoYWtlc3BlYXJlIixsZW5ndGgod29yZCkpCm5ld19zdG9wIDwtIGNiaW5kKHdvcmQsbGV4aWNvbikKc2hha19zdG9wIDwtIHJiaW5kKG5ld19zdG9wLHN0b3Bfd29yZHMpCmBgYAoKSGVyZSBpcyBhIHZpc3VhbGlzYXRpb24gb2YgaG93IHN0b3Agd29yZHMgYWZmZWN0IHRoZSBjb3JwdXMuCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmFzcD0uNSwgbWVzc2FnZT1GQUxTRX0KcDEgPC0gc2hhayAlPiUKICBhc190aWJibGUoLikgJT4lCiAgdW5uZXN0X3Rva2Vucyh0Ymw9LiwgaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSB3b3JkKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4+ODAwKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cmVvcmRlcih3b3JkLG4pLHk9bikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIk5vIHN0b3Agd29yZHMiKQpwMiA8LSBzaGFrICU+JQogIGFzX3RpYmJsZSguKSAlPiUKICB1bm5lc3RfdG9rZW5zKHRibD0uLCBpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IHdvcmQpICU+JQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4+ODAwKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cmVvcmRlcih3b3JkLG4pLHk9bikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIkRlZmF1bHQgc3RvcCB3b3JkcyIpCnAzIDwtIHNoYWsgJT4lCiAgYXNfdGliYmxlKC4pICU+JQogIHVubmVzdF90b2tlbnModGJsPS4sIGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gd29yZCkgJT4lCiAgYW50aV9qb2luKHNoYWtfc3RvcCkgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuPjgwMCkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIod29yZCxuKSx5PW4pKSArCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBnZ3RpdGxlKCJDdXN0b20gc3RvcCB3b3JkcyIpCm11bHRpcGxvdChwMSxwMixwMyxjb2xzPTMpCmBgYAoKIyMgQ29tcGFyaW5nIGFjcm9zcyBwbGF5cwoKQmVmb3JlIHdlIHRyeSB0byBjb21wYXJlIGFjcm9zcyBwbGF5cywgbGV0J3Mgc2VlIHdoYXQgdGhlIG1vc3QgY29tbW9uIGJpZ3JhbXMgYXJlIG92ZXJhbGwgKGFmdGVyIGJlaW5nIGZpbHRlcmVkIGJ5IHRoZSBjdXN0b20gc3RvcCB3b3JkcyBsaXN0KS4KCmBgYHtyfQpzaGFrICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkKYGBgCgojIyMgMS1ncmFtcyAoanVzdCByZWd1bGFyIHRva2VuIGZyZXF1ZW5jeSkKCklmIHdlIGNob29zZSBhIHN1YnNldCBvZiB3b3Jkcywgd2UgY2FuIGxvb2sgYXQgaG93IHRoZXkgYXJlIGRpc3RyaWJ1dGVkIGFjcm9zcyBkaWZmZXJlbnQgcGxheXMuIEluIHRoaXMgY2FzZSwgd2UgY2FuIGNvbXBhcmUgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6I0Y4NzY2RCI+ZGVhdGg8L3NwYW4+LCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojN0NBRTAwIj5raW5nPC9zcGFuPiwgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6IzAwQkZDNCI+bG92ZTwvc3Bhbj4sIGFuZCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojQzc3Q0ZGIj5zd2VldDwvc3Bhbj4gYWNyb3NzIHNpeCBwbGF5cy4gVW5zdXJwcmlzaW5nbHksICpSb21lbyBhbmQgSnVsaWV0KiB1c2VzIHRoZSB3b3JkIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiMwMEJGQzQiPmxvdmU8L3NwYW4+IG1vcmUgdGhhbiBhbnkgb3RoZXIgcGxheSwgYWx0aG91Z2ggKk1pZHN1bW1lciBOaWdodCdzIERyZWFtKiBpcyBjbG9zZS4gPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6IzdDQUUwMCI+S2luZzwvc3Bhbj4gaXMgYWxzbyBtdWNoIG1vcmUgY29tbW9uIGluIHBsYXlzIGFib3V0IGtpbmdzIChzdXJwcmlzZSwgc3VycHJpc2UpLgoKYGBge3J9CnNoYWtbLGMoMiw1LDYpXSAlPiUKICBhc190aWJibGUoKSAlPiUKICB1bm5lc3RfdG9rZW5zKHRibD0uLCBpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IHdvcmQpICU+JQogIGZpbHRlcih3b3JkPT0ibG92ZSIgfCB3b3JkID09ImtpbmciIHwgd29yZD09ImRlYXRoIiB8IHdvcmQ9PSJzd2VldCIpICU+JQogIGdyb3VwX2J5KFBsYXllcixQbGF5LHdvcmQpICU+JQogIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgZmlsdGVyKCAgUGxheSA9PSAiSGFtbGV0IiB8IAogICAgICAgICAgIFBsYXkgPT0gIktpbmcgTGVhciIgfCAKICAgICAgICAgICBQbGF5ID09ICJBIE1pZHN1bW1lciBuaWdodHMgZHJlYW0iIHwgCiAgICAgICAgICAgUGxheSA9PSAiT3RoZWxsbyIgfCAKICAgICAgICAgICBQbGF5ID09ICJIZW5yeSBWIiB8IAogICAgICAgICAgIFBsYXkgPT0gIlJvbWVvIGFuZCBKdWxpZXQiKSAlPiUKICBhcnJhbmdlKGRlc2MobikpICU+JQogIGdncGxvdCguLCBhZXMoeD13b3JkLHk9bikpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsPXdvcmQpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgZmFjZXRfd3JhcCh+UGxheSkKYGBgCgpCdXQgdGhlcmUgYXJlIG1vcmUgaW50ZXJlc3Rpbmcgd29yZHMgeW91IGNvdWxkIGNvbXBhcmUsIGNlcnRhaW5seS4gVGhlc2UgYXJlIGp1c3Qgb25lIGV4YW1wbGUuCgojIyMgQmlncmFtcwoKSXQncyBwcm9iYWJseSBtdWNoIG1vcmUgaW50ZXJlc3RpbmcgdG8gbG9vayBhdCBjb2xsb2NhdGlvbiB0aGFuIHNpbXBsZSB3b3JkIGZyZXF1ZW5jeS4gQWZ0ZXIgYWxsLCBpdCBnaXZlcyBtb3JlIGNvbnRleHQuIEhvd2V2ZXIsIGl0IGFsc28gcmVkdWNlcyB0aGUgbnVtYmVyIG9mIHRva2VucyBzdWJzdGFudGlhbGx5LgoKSGVyZSwgd2UgY2FuIHZpc3VhbGlzZSB0aHJlZSBwYWlycyBvZiBnZW5kZXJlZCBub3VuIHBocmFzZXM6CgpNYXNjdWxpbmUgfCBGZW1pbmluZQotLS0tLS0tIHwgLS0tLS0tLQo8c3BhbiBzdHlsZT0iY29sb3I6I0Y4NzY2RCI+KipteSBsb3JkKio8L3NwYW4+ICAgIHwgPHNwYW4gc3R5bGU9ImNvbG9yOiMwMEJGQzQiPioqbXkgbGFkeSoqPC9zcGFuPgo8c3BhbiBzdHlsZT0iY29sb3I6I0Y4NzY2RCI+KipteSBmYXRoZXIqKjwvc3Bhbj4gIHwgPHNwYW4gc3R5bGU9ImNvbG9yOiMwMEJGQzQiPioqbXkgbW90aGVyKio8L3NwYW4+CjxzcGFuIHN0eWxlPSJjb2xvcjojRjg3NjZEIj4qKm15IGh1c2JhbmQqKjwvc3Bhbj4gfCA8c3BhbiBzdHlsZT0iY29sb3I6IzAwQkZDNCI+KipteSB3aWZlKio8L3NwYW4+CgpXaGF0IGtpbmRzIG9mIGluZm9ybWF0aW9uIGNhbiB3ZSBzZWUgaW4gdGhlIGdyYXBoPwoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5hc3A9LjV9CnNoYWsgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIGZpbHRlcihiaWdyYW09PSJteSBsb3JkIiB8IGJpZ3JhbSA9PSJteSBsYWR5IiB8IGJpZ3JhbT09Im15IG1vdGhlciIgfCBiaWdyYW09PSJteSBmYXRoZXIiIHwgYmlncmFtPT0ibXkgd2lmZSIgfCBiaWdyYW09PSJteSBodXNiYW5kIikgJT4lCiAgbXV0YXRlKGdlbmRlciA9IGJpZ3JhbSkgJT4lCiAgbXV0YXRlKGdlbmRlciA9IHJlY29kZV9mYWN0b3IoZ2VuZGVyLAogICAgICAgICAgICAgICAgYG15IGxvcmRgPSJtYXNjIiwKICAgICAgICAgICAgICAgIGBteSBmYXRoZXJgPSJtYXNjIiwKICAgICAgICAgICAgICAgIGBteSBodXNiYW5kYD0ibWFzYyIsCiAgICAgICAgICAgICAgICBgbXkgbGFkeWA9ImZlbSIsCiAgICAgICAgICAgICAgICBgbXkgbW90aGVyYD0iZmVtIiwKICAgICAgICAgICAgICAgIGBteSB3aWZlYD0iZmVtIikpICU+JQogIGdyb3VwX2J5KFBsYXllcixQbGF5LGJpZ3JhbSxnZW5kZXIpICU+JQogIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgbXV0YXRlKGJpZ3JhbUZhYyA9IGZhY3RvcihiaWdyYW0sIGxldmVscz1jKCJteSBsb3JkIiwgIm15IGh1c2JhbmQiLCAibXkgZmF0aGVyIiwgIm15IGxhZHkiLCAibXkgd2lmZSIsICJteSBtb3RoZXIiKSkpICU+JQogICMgdG9vIGJvcmluZwogIGZpbHRlciggIFBsYXkgIT0gIkhlbnJ5IFZJIFBhcnQgMSIgJgogICAgICAgICAgIFBsYXkgIT0gIkhlbnJ5IFZJIFBhcnQgMiIgJgogICAgICAgICAgIFBsYXkgIT0gIkhlbnJ5IFZJIFBhcnQgMyIgJgogICAgICAgICAgIFBsYXkgIT0gIlBlcmljbGVzIiAmIAogICAgICAgICAgIFBsYXkgIT0gIlRpbW9uIG9mIEF0aGVucyIgJiAKICAgICAgICAgICBQbGF5ICE9ICJUaGUgVGVtcGVzdCIpICU+JQogICMgdG9vIHNrZXdlZAogIGZpbHRlciggIFBsYXkgIT0gIkhhbWxldCIgJgogICAgICAgICAgIFBsYXkgIT0gIlRyb2lsdXMgYW5kIENyZXNzaWRhIiAmCiAgICAgICAgICAgUGxheSAhPSAiUmljaGFyZCBJSUkiICYKICAgICAgICAgICBQbGF5ICE9ICJUaXR1cyBBbmRyb25pY3VzIiAmIAogICAgICAgICAgIFBsYXkgIT0gIkhlbnJ5IFZJSUkiICYgCiAgICAgICAgICAgUGxheSAhPSAiTXVjaCBBZG8gYWJvdXQgbm90aGluZyIpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PWJpZ3JhbUZhYyx5PW4pKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD1nZW5kZXIpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgc2NhbGVfeV9sb2cxMCgpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBmYWNldF93cmFwKH5QbGF5LG5yb3c9MykKYGBgCgoKCiMjIE5ldHdvcmtzCgoKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiaWdyYXBoIikKI2luc3RhbGwucGFja2FnZXMoImdncmFwaCIpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KGdncmFwaCkKbGlicmFyeShncmlkKQoKYSA8LSBncmlkOjphcnJvdyh0eXBlID0gImNsb3NlZCIsIGFuZ2xlPTIyLjUsIGxlbmd0aCA9IHVuaXQoLjEsICJpbmNoZXMiKSkKYGBgCgpJIHRoaW5rIHRoZSBtb3N0IGV4Y2l0aW5nIHdheSB0byB1c2Ugbi1ncmFtcyBpcyBwcm9iYWJseSBuZXR3b3JrIGdyYXBocy4gVGhlc2UgZ3JhcGhzIHNob3cgd2hhdCB3b3JkcyBjby1vY2N1ciwgYW5kIGluIHdoYXQgb3JkZXIuIE1vcmVvdmVyLCB0aGV5IGNhbiBlbmNvZGUgYSBudW1iZXIgb2YgZGltZW5zaW9ucyB2aXN1YWxseSwgd2hpY2ggd291bGQgYmUgdmVyeSBkaWZmaWN1bHQgdG8gY2FsY3VsYXRlIGJ5IGhhbmQgb3IgcGxvdCBpbiBhIG1vcmUgc3RhbmRhcmQgcXVhbnRpdGF0aXZlIG1ldGhvZC4KCiMjIyBCaWdyYW1zCgpGcm9tIHRoZSBsaXN0IG9mIGJpZ3JhbXMgd2UgY2FuIGdlbmVyYXRlIGZyb20gb3VyIGNvcnB1cywgd2UgY2FuIHBsb3QgYSBuZXR3b3JrIGdyYXBoIGluIHdoaWNoIHRoZSBzaGFkZSBvZiB0aGUgY29ubmVjdGlvbiBiZXR3ZWVuIG5vZGVzIGluZGljYXRlcyB0aGUgZnJlcXVlbmN5IG9mIHRoZSBiaWdyYW0gKGRhcmtlciBtZWFucyBtb3JlIGZyZXF1ZW50KS4gTW9yZW92ZXIsIHRoZXNlIGNvbm5lY3Rpb25zICgiZWRnZXMiKSBhcmUgZGlyZWN0aW9uYWwsIHNvIHdlIGNhbiBzZWUgd2hpY2ggb3JkZXIgdGhlIHdvcmRzIGFyZSBvY2N1cmluZyBpbi4KCmBgYHtyfQpzaGFrICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAyMikgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJkYXJrYmx1ZSIsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpCmBgYAoKV2UgY2FuIGFsc28gdXNlIHRoZXNlIHBsb3RzIHRvIGNvbXBhcmUgYWNyb3NzIHBsYXlzIChhbHRob3VnaCB0b2tlbiBmcmVxdWVuY3kgYmVnaW5zIHRvIGRyb3AgcHJlY2lwaXRvdXNseSkuIEhlcmUsIHdlIGNhbiBzZWUgdGhhdCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjpzYWxtb24iPlR3ZWxmdGggTmlnaHQ8L3NwYW4+IGhhcyBub3RhYmxlIGNvbm5lY3Rpb25zIGJldHdlZW4gaXRlbXMgb2YgY2xvdGhpbmcgKHllbGxvdyBzdG9ja2luZ3MsIGNyb3NzIGdhcnRlcmVkKSwgd2hlcmVhcyA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjpsaWdodGJsdWUiPkhhbWxldDwvc3Bhbj4gYW5kIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOmdyZWVuMiI+Um9tZW8gYW5kIEp1bGlldDwvc3Bhbj4gaGF2ZSBtb3JlIHRva2VucyByZWZlcnJpbmcgdG8gcGVvcGxlIGFuZCB0aGVpciBzdGFnZSBkaXJlY3Rpb25zLiA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjpsaWdodGJsdWUiPkhhbWxldDwvc3Bhbj4gYWRkaXRpb25hbGx5IGhhcyBhIG5vdGFibGUgbnVtYmVyIG9mICJmYXRoZXIncyBkZWF0aCIgYmlncmFtcywgd2hpbGUgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6Z3JlZW4yIj5Sb21lbyBhbmQgSnVsaWV0PC9zcGFuPiBtZW50aW9ucyAiY291bnR5IFBhcmlzIiBmcmVxdWVudGx5LgoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5hc3A9LjN9CnAxIDwtIHNoYWsgJT4lCiAgZmlsdGVyKFBsYXk9PSJIYW1sZXQiKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gNikgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJkYXJrYmx1ZSIsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpCgpwMiA8LSBzaGFrICU+JQogIGZpbHRlcihQbGF5ID09ICJUd2VsZnRoIE5pZ2h0IikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDYpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya3JlZCIsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAic2FsbW9uIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpCgpwMyA8LSBzaGFrICU+JQogIGZpbHRlcihQbGF5ID09ICJSb21lbyBhbmQgSnVsaWV0IikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDYpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya2dyZWVuIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJncmVlbjIiLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkKCm11bHRpcGxvdChwMSxwMixwMyxjb2xzPTMpCmBgYAoKSWYgd2UgZXhjbHVkZSBzdGFnZSBkaXJlY3Rpb25zIGFuZCBjb21wYXJlIGFjcm9zcyBzaXggcGxheXMsIHdlIHN0YXJ0IHRvIHNlZSBkaXN0aW5jdCB0aGVtZXMgYXBwZWFyLiAKCioqVXNpbmcgYG11bHRpcGxvdGAsIHRyeSBwbG90dGluZyB0aGUgZm9sbG93aW5nIHNpeCBncmFwaHMgaW4gYSAzIGJ5IDIgbWF0cml4Kio6CgoxLiBIYW1sZXQKMi4gVHdlbGZ0aCBOaWdodAozLiBSb21lbyBhbmQgSnVsaWV0CjQuIE90aGVsbG8KNS4gSGVucnkgSVYKNi4gVGhlIFRlbXBlc3QKCk1ha2Ugc3VyZSBlYWNoIG1lZXRzIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCiogbm8gc3RhZ2UgZGlyZWN0aW9ucwoqIGJpZ3JhbXMgd2l0aCBmcmVxdWVuY3kgZ3JlYXRlciB0aGFuIDMKKiBlYWNoIHdpdGggYSBkaWZmZXJlbnQgY29sb3VyCgoqUmVtZW1iZXIqOiByZXVzaW5nIGNvZGUgYW5kIGFkYXB0aW5nIHNvbWVvbmUgZWxzZSdzIChvcGVuIHNvdXJjZSkgY29kZSBpcyBlbmNvdXJhZ2VkISBBbHdheXMgY2l0ZSB3aGVyZSBhcHByb3ByaWF0ZS4KCmBgYHtyfQojIDEuIEhhbWxldAoKIyAyLiBUd2VsZnRoIE5pZ2h0CgojIDMuIFJvbWVvIGFuZCBKdWxpZXQKCiMgNC4gT3RoZWxsbwoKIyA1LiBIZW5yeSBJVgoKIyA2LiBUaGUgVGVtcGVzdAoKYGBgCgo8YmlnPihwb3NzaWJsZSBzb2x1dGlvbiBiZWxvdyk8L2JpZz4gIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgoKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuYXNwPS41fQpwMSA8LSBzaGFrICU+JQogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIGZpbHRlcihQbGF5PT0iSGFtbGV0IikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDMpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya2JsdWUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWw9VFJVRSkgKyAKICAgIHRoZW1lX3ZvaWQoKSArCiAgICBnZ3RpdGxlKCJIYW1sZXQiKQoKcDIgPC0gc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBmaWx0ZXIoUGxheSA9PSAiVHdlbGZ0aCBOaWdodCIpICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAzKSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgZWRnZV9jb2xvdXI9ImRhcmtyZWQiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gInNhbG1vbiIsIHNpemUgPSA1KSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWw9VFJVRSkgKyAKICAgIHRoZW1lX3ZvaWQoKSArCiAgICBnZ3RpdGxlKCJUd2VsZnRoIE5pZ2h0IikKCnAzIDwtIHNoYWsgJT4lCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIlJvbWVvIGFuZCBKdWxpZXQiKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMykgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJkYXJrZ3JlZW4iLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImdyZWVuMiIsIHNpemUgPSA1KSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWw9VFJVRSkgKyAKICAgIHRoZW1lX3ZvaWQoKSArCiAgICBnZ3RpdGxlKCJSb21lbyBhbmQgSnVsaWV0IikKCnA0IDwtIHNoYWsgJT4lCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIk90aGVsbG8iKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMykgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJkYXJrb3JhbmdlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJvcmFuZ2UiLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkgKwogICAgZ2d0aXRsZSgiT3RoZWxsbyIpCgpwNSA8LSBzaGFrICU+JQogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIGZpbHRlcihQbGF5ID09ICJIZW5yeSBJViIpICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAzKSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgZWRnZV9jb2xvdXI9ImNhZGV0Ymx1ZTQiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImN5YW4iLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkgKwogICAgZ2d0aXRsZSgiSGVucnkgSVYiKQoKcDYgPC0gc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBmaWx0ZXIoUGxheSA9PSAiVGhlIFRlbXBlc3QiKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMykgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJ2aW9sZXQiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gIm1hZ2VudGEiLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkgKwogICAgZ2d0aXRsZSgiVGhlIFRlbXBlc3QiKQoKbXVsdGlwbG90KHAxLHAyLHAzLHA0LHA1LHA2LGNvbHM9MykKYGBgCgoKCiMgSGVhdG1hcHMKCkFub3RoZXIgdmlzdWFsaXNhdGlvbiB0b29sIHRoYXQgY2FuIGJlIHZlcnkgaGVscGZ1bCBmb3Igc2hvd2luZyBkaXN0cmlidXRpb25zIG9mIGV2ZW50cyBhY3Jvc3MgYSBzdHJ1Y3R1cmUgaXMgdGhlIGhlYXRtYXAuIEEgaGVhdG1hcCBpcyBhIGNvbXBsZXggaGlzdG9ncmFtLCB3aGljaCBhbGxvd3MgbXVsdGlwbGUgc3Vic2V0cyBvZiB0aGUgZGF0YSB0byBiZSBjb21wYXJlZCBzaWRlLXRvLXNpZGUuIEluIHRoaXMgZXhhbXBsZSwgd2UgY2FuIGxvb2sgYXQgdGhlIHZhcmlhYmlsaXR5IG9mIGxlbmd0aCBvZiBzdWJzZWN0aW9ucyAoYWN0cyBhbmQgc2NlbmVzKSBhcyBkZXRlcm1pbmVkIGJ5IG51bWJlciBvZiB3b3Jkcy4gSXQgaXMgbXVjaCBlYXNpZXIgYW5kIHF1aWNrZXIgdG8gZXh0cmFjdCB0aGlzIGluZm9ybWF0aW9uIGZyb20gYSBoZWF0bWFwIChhbHRob3VnaCB0aGUgcHJlY2lzZSBudW1iZXJzIGFyZSBsb3N0IGluIHRoaXMgdmVyc2lvbikuCgpMZXQncyBmb2N1cyBpbiBvbiBhIHN1YnNldCBvZiBwbGF5cyBpbiBvcmRlciB0byBzaW1wbGlmeSB0aGUgdmlzdWFsaXNhdGlvbi4gRnJvbSBhIHNpbXBsZSB2aXN1YWwgaW5zcGVjdGlvbiwgaXQgYXBwZWFycyB0aGF0ICoqQWN0IFYqKiB0ZW5kcyB0byBiZSB0aGUgbGlnaHRlc3Qgb24gd29yZHMsIHdpdGggdGhlIHZlcnkgbm90YWJsZSBleGNlcHRpb24gb2YgKkxvdmUncyBMYWJvdXIncyBMb3N0Ki4gT3RoZXJ3aXNlLCAqKkFjdCBJKiogaXMgZ2VuZXJhbGx5IGZhaXJseSBoZWF2eSBvbiB3b3JkcywgYW5kICoqQWN0IElJKiogdGVuZHMgdG8gYmUgYSBiaXQgbGlnaHRlci4gQXJtZWQgd2l0aCB0aGF0IChzdXBlcmZpY2lhbCkgb2JzZXJ2YXRpb24sIHdlIG1pZ2h0IGJlIGFibGUgdG8gY2hlY2sgd2hldGhlciBvdXIgZXllcyBkZWNlaXZlIHVzIG9yIHdoZXRoZXIgdGhlcmUncyBzb21ldGhpbmcgdG8gaXQuIChCdXQgSSdsbCBsZWF2ZSB0aGF0IGV4cGxvcmF0aW9uIGZvciBhbm90aGVyIHRpbWUuKQoKYGBge3J9CnNoYWsgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIkhhbWxldCIgfCBQbGF5ID09ICJLaW5nIEpvaG4iIHwgCiAgICAgICAgICAgUGxheSA9PSAiVGhlIFRlbXBlc3QiIHwgUGxheSA9PSAiQ3ltYmVsaW5lIiB8IAogICAgICAgICAgIFBsYXkgPT0gIk1lYXN1cmUgZm9yIG1lYXN1cmUiIHwgUGxheSA9PSAiVGltb24gb2YgQXRoZW5zIiB8IAogICAgICAgICAgIFBsYXkgPT0gIlJpY2hhcmQgSUlJIiB8IFBsYXkgPT0gIkxvdmVzIExhYm91cnMgTG9zdCIgfCAKICAgICAgICAgICBQbGF5ID09ICJBIFdpbnRlcnMgVGFsZSIgfCBQbGF5ID09ICJPdGhlbGxvIiB8IAogICAgICAgICAgIFBsYXkgPT0gIlJvbWVvIGFuZCBKdWxpZXQiIHwgUGxheSA9PSAiSGVucnkgViIpICU+JSAKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBtdXRhdGUoQWN0U2NlbmVMaW5lMiA9IEFjdFNjZW5lTGluZSkgJT4lCiAgc2VwYXJhdGUoQWN0U2NlbmVMaW5lMiwgYygiYWN0IiwgInNjZW5lIiwgImxpbmUiKSkgJT4lCiAgY291bnQoUGxheSxhY3QsIHNvcnQ9VFJVRSkgJT4lCiAgdHJhbnNtdXRlKHBsYXk9UGxheSwgYWN0PWFzLmludGVnZXIoYWN0KSwgbj1uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YWN0LHk9cmVvcmRlcihwbGF5LCBuKSkpICsgCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBuKSwgY29sb3VyID0gIndoaXRlIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAic3RlZWxibHVlIikKYGBgCgoKTWF5YmUgdGhlcmUgaXMgc29tZSBwYXR0ZXJuIHdlIGNhbiBkZXRlY3QgYnkgcGxvdHRpbmcgdGhlIG51bWJlciBvZiB3b3JkcyBieSBzY2VuZSwgYnkgYWN0LCBhbmQgYnkgcGxheWVyIHdpdGhpbiBhIHNpbmdsZSBwbGF5ICgqSGFtbGV0KikuIFRoaXMgZmlndXJlIGlzIGFibGUgdG8gY29tbXVuaWNhdGUgYSB0cmVtZW5kb3VzIGFtb3VudCBvZiBpbmZvcm1hdGlvbiBpbiBhIHZlcnkgc21hbGwgc3BhY2UgYnkgaWxsdXN0cmF0aW5nIHdoZXJlIGVhY2ggY2hhcmFjdGVyIGhhcyB0aGUgYnVsayBvZiB0aGVpciBsaW5lcy4gSGVyZSwgd2UgY2FuIHNlZSBIYW1sZXQgaXMgcG9zaXRpdmVseSB2ZXJib3NlIGluICoqU2NlbmUgSUkqKiBpbiAqKkFjdHMgSUksIElJSSwgVioqLCBidXQgb3RoZXJ3aXNlIG9ubHkgbWFyZ2luYWxseSB3b3JkaWVyIHRoYW4gdGhlIG90aGVyIHBsYXllcnMuIE1vcmVvdmVyLCB0aGVyZSBzZWVtcyB0byBiZSB0aGUgbW9zdCBldmVuIGRpc3RyaWJ1dGlvbiBvZiBsaW5lcyAobmVpdGhlciB2ZXJ5IGRhcmsgbm9yIGVudGlyZWx5IHBhbGUsIGFzIGNvbG91ci1jb2RlZCkgaW4gKipBY3QgSSoqLCB3aGVyZWFzIHRoZSBvdGhlciBhY3RzIGFyZSBxdWl0ZSBza2V3ZWQgdG93YXJkIHRoZSBmZXcgbWFpbiBwbGF5ZXJzLgoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5hc3A9LjV9CnNoYWsgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIkhhbWxldCIpICU+JSAKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBtdXRhdGUoQWN0U2NlbmVMaW5lMiA9IEFjdFNjZW5lTGluZSkgJT4lCiAgc2VwYXJhdGUoQWN0U2NlbmVMaW5lMiwgYygiYWN0IiwgInNjZW5lIiwgImxpbmUiKSkgJT4lCiAgY291bnQoUGxheWVyLGFjdCxzY2VuZSwgc29ydD1UUlVFKSAlPiUKICB0cmFuc211dGUocGxheWVyPVBsYXllciwgYWN0PWFzLmludGVnZXIoYWN0KSwgc2NlbmU9YXMuaW50ZWdlcihzY2VuZSksIG49bikgJT4lCiAgZ2dwbG90KGFlcyh4PXNjZW5lLHk9cmVvcmRlcihwbGF5ZXIsbikpKSArIAogICAgZ2VvbV90aWxlKGFlcyhmaWxsID0gbiksIGNvbG91ciA9ICJ3aGl0ZSIpICsgCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkMiIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygwOjgpKSArCiAgICB0aGVtZV9kYXJrKCkgKyAKICAgIGZhY2V0X3dyYXAofmFjdCwgbmNvbCA9IDUpCmBgYAoKCgpOb3csIGNob29zZSBhIGRpZmZlcmVudCBwbGF5IGFuZCBjcmVhdGUgeW91ciBvd24gdmVyc2lvbiBvZiB0aGlzIGtpbmQgb2YgZ3JhcGguICoqV2hhdCBjYW4geW91IGxlYXJuIGFib3V0IHRoZSBjb250cmlidXRpb24gb2YgZWFjaCBvZiB0aGUgcGxheWVycyBmcm9tIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlaXIgbGluZXM/KioKCmBgYHtyfQoKCmBgYAo=